Developer documentation

Integrate your game with VPlay

Everything needed to publish an HTML5 game, report scores, and enter it into tournaments. Scores are optional — a game runs fine without them — but they are required for leaderboards and tournaments.

Quick start

  1. Create a developer account.
  2. Host your game build on any public HTTPS URL that loads in an iframe.
  3. Submit it from your dashboard with a title, description and category.
  4. Optional: add the six-line score snippet in Sending a score.
  5. We review it, then it goes live and starts earning.

You keep hosting the build

VPlay embeds your game from your own URL rather than ingesting the files. Your build stays where you put it, and it must remain reachable over HTTPS for the game to keep working. See After approval for what this means for updates.

Game requirements

HTTPS only

The URL must be https://. Plain HTTP is rejected at submission — a browser blocks it inside our page anyway.

Must run in an iframe

Your host must not send X-Frame-Options: DENY or a frame-ancestors policy that excludes us. This is the single most common reason a submitted game shows a blank player.

Self-contained

No installers, no plugins, no downloads. WebGL, Canvas and WebAssembly are all fine.

Responsive

Playable on phones as well as desktop. Tell us if it is portrait-only or landscape-only and we set the player orientation to match.

Your rights

You must own or be licensed to distribute everything in the build, including audio and fonts. Submitting content you do not have the rights to gets the account banned.

No external ads

Do not embed your own ad network. Monetisation is handled by the platform and shared with you — see Revenue share.

Engines we routinely accept: Unity WebGL, Godot HTML5, Construct 3, Phaser, PixiJS, Three.js, PlayCanvas, Cocos Creator. Anything that compiles to a web build works.

Sending a score

One postMessage to the parent window. There is no SDK to install, no API key, and no script tag — which means nothing to keep up to date.

// Call this ONCE, when a run genuinely ends.
function submitScore(score) {
  window.parent.postMessage(
    { type: 'VPLAY_SCORE', score: Math.floor(score) },
    '*'
  )
}

// Example: game over
function onGameOver(finalScore) {
  submitScore(finalScore)
}

type

Must be exactly VPLAY_SCORE.

score

A non-negative integer. See the rules.

Emit on a final result only

Not every frame, point, or level tick. The host throttles to one submission per five seconds and keeps the first message in each window, not the last — so a stray mid-run emit will suppress the genuine end-of-run score that follows.

The rules

These apply to both destinations — the all-time leaderboard and tournaments. They exist because scores arrive from the player's browser and cannot be trusted on their own.

Integers only

The host floors your value, and the API rejects non-integers outright. If your score is a time, send milliseconds or centiseconds — never a float.

The player must be signed in

Anonymous plays return 401 and are dropped silently. This is normal and not an error in your integration.

Never emit from a nested iframe or a worker

The host checks the message source against your game frame and discards anything else.

A valid play token must exist

The host mints one automatically, once, shortly after your iframe fires load; it lasts six hours. Without it a score is rejected with no_duration. A document that never fires load can never submit a score.

A run must last at least 5 seconds

Rejected as too_fast otherwise. Measured by the server from when the play token was issued — once per page load, not once per run — so this mainly catches an emit on a splash or menu screen.

The score must fit the time played

The ceiling is ceil(effectiveMax × seconds / 60): half the maximum after 30 seconds, all of it after a minute. Exceeding it is rejected as rate_exceeded. This check always applies — with no scoreMax configured it falls back to the platform ceiling of 10,000,000, which still caps a 10-second-old token at roughly 1.67M.

Rate limit

12 submissions per minute, then HTTP 429. Counted per player and game for the leaderboard, and per player and tournament for tournaments. Do not retry in a loop.

Score ceilings and scoreMax

DestinationCeiling
LeaderboardA hard 10,000,000, checked first and never raised. When scoreMax is set the lower of the two applies.
TournamentsYour game's scoreMax plus the time-scaled rule above. scoreMax cannot exceed 10,000,000.
A score of 0Skips the plausibility ceiling, but not the play-token requirement — a 0 with no token is still rejected as no_duration. On a lower-is-better tournament, 0 is the winning score, so it is verified like any other.

Tell us two numbers before launch

Your game's realistic maximum score, and roughly what a strong player reaches after 10 seconds, 30 seconds, and a full run. We set scoreMax from those. Getting it wrong is the one rule that can reject honest players — if a genuine playthrough is ever refused, that is our threshold being wrong, not your game. Send us the run and we will retune it.

Tournaments

Timed competitions with entry fees and prizes. No other portal in the field offers them, and any scoring game can be entered. Tournaments apply the same protocol with three differences that matter to you.

Direction is configured per game, not by you

Set your game to low and a smaller number wins — the entry keeps the lowest score and ranking sorts ascending. Ties break by who reached the score first, so placings are deterministic. Send the raw value and never invert it yourself, or it is inverted twice.

Rejections are recorded against the player

A rejection stamps their entry. At payout, a flagged player's prize is held for manual review instead of being paid when it is above 500 crowns, or when the band awards a coupon of any value — a code cannot be clawed back once issued.

Attempts

A submission the server accepts consumes one attempt even when it does not improve the score. A rejected submission consumes no attempt but leaves the flag, which is worse.

If your game is lower-is-better

Your tournament standings will be correct, but the all-time leaderboard keeps the highest value and will show your worst run as the best. That is a known limitation of the season leaderboard, not your integration — tell us and we will exclude the game from the leaderboard rather than display nonsense.

To have a game enabled for tournaments, send us the game and the two scoreMax numbers above. We set supportsScoring and the direction on our side — there is nothing to change in your build.

Host → game messages

The player sends these to your frame. Handling them is optional but strongly recommended — without them a game keeps playing audio behind a modal.

window.addEventListener('message', (e) => {
  if (e.data?.type === 'VPLAY_PAUSE')  pauseGame()
  if (e.data?.type === 'VPLAY_RESUME') resumeGame()
  if (e.data?.type === 'VPLAY_MUTE')   setMuted(!!e.data.muted)
})

Testing

  1. Confirm your build loads over HTTPS inside a plain local iframe.
  2. Check the browser console for frame-blocking errors — these are the usual cause of a blank player.
  3. Verify you emit VPLAY_SCORE exactly once per finished run, at least five seconds after load, with an integer.
  4. After approval, play the live game signed in and confirm your score appears.

There is no public sandbox yet

We do not currently offer a self-serve test tournament or a staging key. To test the tournament path before launch, ask us and we will set one up for you. We would rather say this plainly than have you hunt for a sandbox that does not exist.

Submitting and review

Submit from your dashboard. Every submission is reviewed by a person before it goes live — nothing publishes automatically.

What we check

That the game loads and is playable, that it is yours to publish, that it is age-appropriate, and that the URL is reachable over HTTPS.

What we set for you

Categories, orientation, and — if you want scores — supportsScoring, score direction and scoreMax.

Review time

We aim to review within a few working days and will tell you the outcome in the portal. If a submission is rejected you will be told why, and you can resubmit.

After approval — what you can change

FieldSelf-serve?How
Description, tags, orientationYesDashboard → your game → Edit
The game build itselfNot yetYou host the build, so shipping a new version at the same URL updates the live game immediately — no action needed here. Changing the URL requires us.
Thumbnail, title, categoriesNot yetContact us and we will change it

Known limitation, being worked on

Self-serve upload of a new build or thumbnail is not available yet — the dashboard can only take assets from our own storage, and there is no developer uploader. Until it ships, publish updates at your existing URL, or contact us. We would rather document this than let you discover it mid-launch.

Revenue share

Your share

50% of net advertising revenue attributable to your games. This is at or above the norm for HTML5 portals, which typically run 33–50%.

Reporting

Your dashboard shows plays, unique players, daily trends and traffic sources. The earnings figure shown there is labelled an estimate and is not a payable balance.

Payouts are not automated yet

There is no self-serve payout pipeline today — no payment details are captured at registration and no automatic threshold exists. Revenue is tracked and settled with you directly. Talk to us before you plan around a payout schedule.

Troubleshooting

SymptomCause
Blank playerYour host blocks framing (X-Frame-Options / frame-ancestors), or the URL is not HTTPS.
Score never appearsThe player is signed out, or you emit before the 5-second minimum, or the value is not an integer.
reason: no_durationNo valid play token — usually the iframe never fired load, or the score was sent too early.
reason: too_fastThe run was under 5 seconds, measured from when the play token was issued.
reason: rate_exceededThe score is too high for the time played. If it was a genuine run, tell us and we will retune scoreMax.
HTTP 429More than 12 submissions in a minute. Emit once per finished run.
Only the first score of a run countsThe 5-second throttle keeps the FIRST message in each window. Remove mid-run emits.
Leaderboard shows my worst runThe game is lower-is-better. Ask us to exclude it from the all-time leaderboard.

Something unclear, or a rule behaving differently than described here?

Contact the team