Choose how your game sits on the screen

Where your game runs, how it sits on the screen, and how it loads smoothly and fast.

Decide two things before you build: where your game will be opened, and how it occupies the screen there. They change how the game is built, from how big the text can be to what happens to a long list, and they are expensive to change later.

npx gamestage create --name "Kick Off" --format hunt --host app-webview

create records both in gamestage.yaml, and verify holds the game to them.

Where it runs

--hostWhere the game is openedDefault layout
fullscreenFull screen, from a carousel, a Hub or a link. The defaultscreen (page for predict)
article-launchA card in an article that opens the game full screen over itlaunch
article-embedA box inside an article, played where it sitscard
app-webviewInside a club's or publisher's own app, in a web viewscreen (page for predict)

How it sits on the screen

--layoutWhat the player getsUse it for
screenFills the phone, nothing scrolls, and the layout adapts to the phone's sizeAlmost every game
canvasDrawn at one size and scaled to fit, with bars where the shape differsGames that need exact positions
launchA preview in the host page; tapping it opens the game full screen, and closing it returns the reader to where they wereAny game shown in an article
cardA fixed box inside a page that scrolls, with no scrolling inside itOnly where the host can't open a game full screen
pageScrolls like a web pageLong question lists and results tables
sheetSlides up over part of a host appOne quick question inside an app

Choose full screen. Players come back to a game they can play properly. Full screen, a game feels like an app and gets played; a widget in an article reads as throwaway and gets scrolled past. In an article, use launch, and keep card for a host that can't allow anything else: a box in an article is small, easy to scroll past, and the page's own scrolling fights swipes inside the game.

Gamestage UI's Stage takes the same words as fit, so a game built with it says this once.

Load smoothly

A fan should see your game's colour from the first frame, then a loading mark, then the game in one step. Never a white page, bare "Loading…" text, or a board that jumps in.

Scaffolds from create do all of this already. If you wrote your own page:

  1. Paint the game's colour before any script. In the head, before the first <script>:

html <meta name="color-scheme" content="dark" /> <meta name="theme-color" content="#0B1B2B" /> <style>html, body { background: #0B1B2B; margin: 0; }</style>

  1. Draw a loading state, never text. A centred spinner, or a progress bar when you know how far through you are, in a box the size of the game. Gamestage UI's Loading is exactly this.
  2. Say when the game is playable. When the round is drawn, or has failed, call window.gamestageReady() (scaffolds define it), or set the mark yourself: performance.mark("gamestage:playable"). Remove the loading state then, not before.

Intro screen

Every game create scaffolds opens on an intro screen: a full-screen colour gradient, your game's name in its own font, a progress bar while it loads, then a Play button. The tap on Play is what starts the game, which is also the gesture a browser needs before it will let a page play sound. A game scaffolded before this feature shipped keeps its old start.

The window.gamestageReady() call from the previous section is also what drives the intro screen. Call it with an argument to say more than "ready":

CallWhat the fan sees
window.gamestageReady()The round is drawn; the button reads "Play"
window.gamestageReady({ resume: true })The fan has a round already in progress; the button reads "Continue"
window.gamestageReady({ failed: true })The game could not load; the screen shows its error text and a retry button

Wait for the tap before you open anything that asks the fan something, such as How to play. window.gamestageStart is a promise that resolves on Play, and gamestage:start is the matching event; opening a dialog before then would be asking the fan before they have agreed to start.

The colours and words are Studio settings, under Brand and Wording. Set them there, or seed them in gamestage.settings.json before your first deploy the same as any other field (see Beside the manifest: `gamestage.settings.json`); create already seeds the four button and error labels for you.

SettingField keyWhat it changes
Intro screen colour, topsplash_gradient_startTop of the gradient, and the colour the page paints before anything else loads. Empty uses your background colour
Intro screen colour, bottomsplash_gradient_endBottom of the gradient. Empty uses your background container colour
Intro screen buttonsplash_play_labelWhat Play says. Empty says "Play"
Intro screen button, part way throughsplash_continue_labelWhat the button says when the fan has an unfinished round. Empty says "Continue"
Intro screen, when loading failssplash_error_textEmpty says "The game didn't load. Check your connection and try again."
Intro screen, try again buttonsplash_retry_labelEmpty says "Try again"

The name is drawn in its own typeface here too, from the same Brand settings that style it everywhere else on the page: see Theming: the title's own typeface for the typeface, weight, letter spacing, case, colour, and the separate logo text and accent colour settings.

Inside a native app

Tell the app's team two things, because they are in the app rather than your page:

  • Set the web view's own background to your game's background colour. A web view is white until the page paints, so without this the player sees a white flash first.
  • Create the web view before the player taps, and load the game into it then. Starting it on the tap adds the whole start-up to the wait.

Load fast

gamestage verify opens your game on a mid-range phone over 4G (the processor slowed four times, 9 Mbit/s down, 60 ms round trips, nothing cached) and reports what it downloaded and how long it took. Over a budget, it warns and names the heaviest files. It never fails a game for being slow.

LayoutJavaScriptCSSFontsEverythingFirst renderPlayable
screen350 KB60 KB150 KB900 KB1.8 s3.5 s
canvas500 KB60 KB150 KB1,500 KB1.8 s4.5 s
launch300 KB50 KB120 KB700 KB1.5 s3.0 s
card300 KB50 KB120 KB700 KB1.5 s3.0 s
page400 KB80 KB150 KB1,100 KB1.8 s4.0 s
sheet250 KB40 KB100 KB600 KB1.2 s2.5 s

Sizes are what crossed the network, compressed. First render is when the fan first sees something that isn't blank, so painting your colour and a loading mark from the HTML counts. Playable is your gamestage:playable mark, or when verify first finds the board if you set none.

What usually makes a game heavy, and the fix:

  • Fonts: load one family in two weights, not five, and subset it to the characters the game uses.
  • Components you don't use: install only the Gamestage UI components the game draws.
  • Images: size them for a phone and use WebP.

verify also checks a screen, canvas, card or sheet game fits at 360×640, 390×844 and landscape without the page scrolling. If a list must scroll, let it scroll inside the game, never the page.