UI that looks like your game
Screens of the game's own, not the kit's: art direction, palette, type, a signature, Flow's screens restyled.
The kits' screens and Flow's stock ones (the title, the lobby list, the 3-2-1, the round banner, the "1/3" counter, the scoreboard and the podium) are placeholders. They work and they read, and they are the same in every game, so a game that keeps them looks like every other game made from a kit. Players judge a game by its first screen; make every screen belong to your game's world.
The process: decide, then draw
Write each step into DESIGN.md (its "Art direction" and "The UI") before drawing anything.
- An art direction, from the game's world. Name the world and what it is made of: "a lacquered tea house at night", "a chalk diagram on a school blackboard", "a pinball table", "a deep-sea expedition log". Every screen is made of that stuff.
- A palette from that world: five to seven colours with hex codes. A ground, an ink, one or two accents, a good and a bad. Pull them from the world (brass, felt, lacquer red, chalk white), not from a generic UI kit. Players' colours come with symbols.
- A typeface that fits, not the engine's default Sora: a display face for big words (titles, results) and a plain one for
numbers. In the theme's
font, or a label's or a Text's ownfont: any Google Font by name (one with a single weight, like Bangers or Bebas Neue, works too), a font file in the game's folder by its path ('fonts/Pixel Hero.woff2'; .woff2, .woff, .ttf), or a family your CSS declares with @font-face. Each loads the first time text uses it, and the HTML overlay takes the theme's. Symbols (● ▲ ★ ♛, emoji) are in no font the engine brings: a browser borrows them from the device (different on every phone, or a box). Draw them as shapes, or pick a font that has them;shotandcheckname the ones your text uses. - One signature element no other game has, used everywhere: a ribbon the title, the round and the winner are painted on; a chalk underline; a brass dial for the clock; stamps for results. It is what someone remembers from a screenshot.
- Diegetic where it fits: put information in the world rather than over it. The score on a scoreboard in the scene, the round as notches on the table's edge, whose turn it is as a spotlight, the countdown as the referee's raised fingers.
- Set the theme from it:
UI({ theme: { font, colors: { ink, panel, panelAlt, text, onAccent, dim, accent, accent2 }, radius, border, shadow } }). Even the panels you keep then read as yours, not as the engine's dark navy, and the platform's own pieces in your screen (its lobby strip, the touch controls) take youraccent,panelandtext.
Flow's screens, your look
Flow keeps the logic (when each screen shows, its timing, sounds, buttons, the lists other players send). You give the look, one screen at a time, with a function of that screen's data:
UI({
title: 'TIDE POOL',
theme,
flowViews: {
title: (s) => UI.column({ align: 'center', gap: 18, opacity: s.enter },
UI.label(s.title, { size: 72, color: 'accent', font: 'Fraunces' }),
UI.button('DIVE IN', { kind: 'primary', ...s.play })), // Flow's Play: its key, Enter, its sound
countdown: (s) => UI.label(s.go ? 'SPLASH!' : String(s.n), { size: 150 - 50 * s.beat, color: 'accent' }),
banner: { draw: (s) => drawBanner(s), anchor: 'top' }, // s.goal, s.round, s.rounds, s.enter, s.leave
hud: false, // the round lives in your own HUD, your way
scoreboard: (s) => drawStandings(s.rows), // rows: name, points, place, me, bot, avatar
results: (s) => drawPodium(s.podium, s.awards, s.again),
lobby: (s) => drawLobby(s.players, s.settings), // settings' options have chosen and choose()
player: (p) => drawSeat(p), // how a player shows on any stock screen left
},
});
- Each look gets the screen's data (
s) and the view context; it returns UI nodes, or draws withUI.customand the painter. { draw, anchor, offset }moves a screen;falsedrops one (then show what it showed somewhere of yours).onceworlds api FlowViewSwitcheslists every screen's data.- Your own HUD is a
ui.viewof yours. Readflow.round,flow.roundsand the time left from the view context. - A game with no Flow shell (
Flow.none(), or none at all) draws every screen it has withui.view: the same process applies.
What makes UI look generic (avoid it)
- Dark navy pill panels on everything. The default panel colour boxed round every HUD element, card and button. Use your palette, and let most information sit on the scene with an outline or a shape of the world behind it, not in a box.
- A "1/3" in a corner. Show the round in the game's language: notches, candles, three cards with one turned, a tide line.
- Name pills with BOT and YOU badges. Show a player as the thing they are in your game: their piece, their colour and symbol, their avatar in a frame from your world. Mark yourself with light, size or a halo, not a yellow tag. Bots needn't be labelled.
- A title card with a tagline over a dimmed screen, a yellow bar and a PLAY button. Make the title a moment of the world: the name carved, painted or lit in it, the game playing behind it, and one big button in your style.
- The same HUD layout in every game: a panel at the top centre, a timer, a counter. Put each piece where the eye already is (the clock by the ball, your score by your piece) and drop what the scene already shows.
- Two countdowns. Flow's 3-2-1 is the only one: restyle it, never draw another (
checkwarns). - More words. Labels of one to three words, at the moment they matter, then gone. Icons and shapes before sentences.
Judge it with shot
npx @onceworlds/cli shot --phase title,--phase lobby, thenshotandshot --size 390x844(a phone held upright).shot --storylays the first minute out on one sheet (title, lobby, countdown, goal, first beats, first result): every frame should look like the same game, and like no other.- Ask of each picture: could you tell this game from the kit at thumbnail size? Is there one palette, one type, one signature? Is any panel the engine's navy, any "1/3", any BOT pill left? Is every word big enough on a phone (18 px) and in strong contrast?
checknames what is still stock (stock-screens,stock-theme), with the line that changes it.