One- or Two-Button Game Design Guide
Building accessible games and apps for Ben and families like ours. This guide provides comprehensive principles, input models, and best practices for creating experiences that work with just two keys: Spacebar and Return.
This page is the overview. The full implementation reference — shared module APIs, the exact input contract, per-game input models, and known traps — lives in bennyshub/ACCESSIBILITY.md in the repo. Where the two ever disagree, that file is the authority, because it is written from the shipped code.
Quick Navigation
Core Principles
- Entire app and every game must be fully usable with only two keys: Spacebar and Return, plus optional mouse or touch for caregivers or users who have those access options.
- And fully usable with one key. With Auto Scan on, Return alone must reach everything. One switch is the real floor, not two.
- Preserve working gameplay and add access options where practical. Keep existing aim, charge, timing and hold mechanics. A separately scoped tap-friendly option can offer another way to play while keeping the same rules and difficulty. Some games may retain hold requirements; record them accurately. The scan upgrade changes stationary choices only.
- All actions should be available through scanning and selection.
- Independence is the measure. A player must get from the hub's front page, into a game, through its menus, into a round, out to pause and settings, and back to the hub, alone. A screen that quietly needs a second person is a serious bug.
- "One switch" describes an input, not a person. Build for the range: difficulty options, no-fail modes, and editors that let a caregiver tune content to the player.
Input Model
Spacebar Controls
- Short press (release): Move highlight forward in the current scan list. Activates on release so accidental holds don't trigger selection.
- Hold: In Step, scan backwards at the chosen speed. In Auto, Space Brake may own the press and release — see Scanning Behavior.
Return Controls
- Short press (release): Select the currently highlighted item.
- Hold: Open the pause or context menu, or exit a sub-mode. Use this for options, help, or a safe back button. Always pair it with an on-screen button that does the same thing.
Mouse or Touch (Optional)
- For caregiver support or for individuals who have mouse, touch, or eye-tracking access.
- Click to select, click and hold to open context menus when appropriate.
- Never require dragging.
Scanning Behavior
Choose Auto Scan and scan speed once in Hub Settings. Every stationary choice context follows the rules below; gameplay keeps its existing controls.
Required park step
"If Ben stops pressing, does anything in the game keep happening?" No means a CHOICE scan: apply all three features. Yes means a gameplay MECHANIC: preserve it.
Every root choice loop includes a blank -1 in both directions. In Step mode it clears highlights silently and Enter does nothing. In Auto, Parking Off passes through silently for one full interval and continues; only Park when Chosen or Auto Park speaks “park” when TTS is on. Park when Chosen parks on Enter; Auto Park stops after 1, 2 or 3 complete loops. Parked clears all highlights, shows a visible Parked badge, and Enter resumes the first item without selecting. Preserve native hold-to-pause.
P3GL’s optional before-shot choice order is blank, Pause/Options, Take Shot; its aim sweep is unchanged. Racer’s stationary menus use the rule, while live steering and native multiplayer controls remain mechanics.
Space brake
With Auto and Space Brake on, freeze the current choice on Space press so its label can finish. This is the act-on-release exception. A tap stays paused; Enter selects; a second tap or a held Space release resumes after one full interval. Show only a dotted outline on the paused scan choice, including after scrolling; do not add Paused text or a Paused badge. Keep the Parked badge. Speak Paused after the owned label ends normally; cancel stale notices. On blank or parked, Space does not select. Brake Off preserves the old Auto Space behavior. Never intercept a multiplayer player’s selection switch.
Wait for speech
When enabled, advance after the owned label ends plus a full scan interval. Input remains available. Disabled or failed speech uses ordinary timing. The voice manager bounds completion using text length/rate with a 10-second cap; the cap releases the wait without cutting off narration. A stale completion cannot move another menu.
Focus and settings
Fresh menus open blank. Changing an option or redrawing a live list retains stable item identity. Back within a task restores its parent row/item; a separate modal return may start fresh. Keyboard rows opt into enterGroup(children, {wrap:true}): forward, Auto and held-Space reverse scans wrap within that row until selection or Back. These local cycles do not count toward root parking. Other child groups exit to root blank at their boundary. Removed items clear safely without selecting anything.
Parking, Loops Before Parking, Space Brake and Wait for Speech appear only in Hub Settings. Keep them visible with descriptions and disabled when Auto is off; Loops also requires Auto Park. Keep the original page scrolling. Existing app Auto, voice and speed controls still use the same managers, with live updates across the Hub, frames, tabs and Companion.
Shared input and platform contract
Use NarbeScanManager.createChoiceScan or the explicit NarbeChoiceScanAdapter with stable IDs, labels, a status host and the existing native hold threshold. Suspend it when gameplay, typing or a native dialog owns input. The shared controller installs no keyboard listeners or extra debounce.
Input Sensitivity is the existing 50/100/200/300 ms cooldown and anti-rapid filter, default 50 ms. It is not a minimum press duration; never add another gate. Scan intervals remain 1/2/3/4 seconds. Hold thresholds remain owned by each app.
NarbePlatform isolates storage and transport, speech start/end/error, key capture and lifecycle. Shared policy uses that contract; the future Electron adapter supplies its own implementation. Electron remains a separately authorized pass.
Visibility and Audio
Visual Design
- High-contrast UI by default, large hit targets, generous spacing
- Never signal state with colour alone, and make the highlight unmistakable — colour and thickness
- The hub itself offers UI size (Default, Large, Larger, Largest), highlight colour (6), highlight style (Outline or Full Cell), text colour (7) and background theme (11). Match that vocabulary rather than inventing your own naming
Text-to-Speech
- TTS for all labels, menu names, states, and outcomes, through
NarbeVoiceManager - Speak on focus and on selection, and speak outcomes
- Treat the voice manager as the single source of truth for whether TTS is on and which voice is selected — never keep your own copy of that state
- Expose TTS on/off and Voice in your settings. Rate, pitch and volume exist in the manager and can be passed per-utterance, but no game currently surfaces them as controls
- Keep spoken labels short — they are read at every scan step, and at a 1-second scan speed a long label becomes a drone
- TTS should never block input
Sound Effects
- Use
SafeAudio, never the Web Audio API — anAudioContextcan take down the renderer in the Electron desktop build SafeAudio.preload(name)with no URL synthesises a built-in blip. Passing a URL that 404s permanently shadows the built-in, and it fails silently
Layout and Components
- Help, Settings and Exit to Main Menu must be reachable from the game's own menus — put them wherever they fit the game
- Footer: Current mode, current scan target name, and a simple legend:
Space = next,Return = select,Return hold = menu - All dialogs and menus scannable using the same pattern as gameplay
- Large buttons, minimum 64px height on tablet
- Avoid small tap targets
- Never require keyboard keys other than Spacebar and Return. These two are hard-coded throughout the hub and there is no remapping setting — most switch interfaces are configurable on the hardware side, so a box that sends the wrong key is fixed there. If remapping is ever added it belongs in
scan-manager.jsas a central translation layer, not in each game
Game Mechanics That Work Well
Golden rule: make the precision and reaction demand optional — do not throw out the genre, and do not throw out the challenge either. Almost any kind of game can be made switch-accessible. What cannot be made accessible is a control that requires fast, accurate, continuous input with no other route through it. If a mechanic needs one, build the alternative alongside it and let a setting choose.
An earlier version of this guide ruled out whole genres — racing, action, anything with a fail state. The hub has since shipped all of them successfully, so the guidance below reflects what actually works in practice.
Where the existing games stand, honestly. Benny's Hub was built around what Ben can do. The controls were tuned to one person's movements and tested against a real body every day rather than against a guideline, which is a large part of why they work as well as they do. The reach turned out to be far wider than one player, and that is why the hub was opened up at all.
The cost of building that way is that the places where other people's capabilities differ did not always get designed for. Charge-and-release is the clearest example: it suits Ben, it suits a lot of players, and for someone who cannot sustain a hold it is simply a closed door. That is not a flaw in those games — they do what they were built to do — it is the next thing to build.
So some games in the hub do not yet offer the no-hold alternative, and will be updated as they are revisited. Football's Easy Throw already does it. The guidance below is what to follow for anything new, and the direction the rest is heading — it is not a report on what every game does today.
The patterns that make hard genres work
These four patterns are what let a one-switch player do things that normally need a mouse and fast hands. Reach for them before concluding a mechanic is impossible.
- Oscillate and stop — instead of aiming, a marker travels along the arc and the player stops it where they want it. This replaces pointing entirely. It has two forms, and a game should offer whichever matches the player's scheme: player-driven (the marker moves only while the switch is held, and each press reverses it — full control, needs a hold) and self-driven (the marker sweeps continuously and a single press commits — needs no hold at all). Bowling, P3GL and Basketball Shooter all use this; P3GL switches between the two forms with Auto Scan
- Charge and release — instead of setting a power level, hold and let go; longer hold, more power, on a non-linear curve so short taps stay usable. Bowling, Football, Baseball, Mini Golf. Build a no-hold alternative beside it, behind a setting — two options, charge or don't. The method is to separate the decision from the execution and keep only the decision. Football is the worked example: a throw is scan the receivers, select one, and then a hold for power.
Easy Throwdrops the hold and keeps the selection — pick the receiver and it throws at ideal power — so a player who cannot sustain a hold still plays the whole season. Where an action has no selection under the charge, give it one (a named swing or power level) rather than leaving the player nothing to choose. It sits in both the main-menu and the pause settings and persists — that is the shape to copy - Row, then column — two-stage scanning reaches any 2-D grid with one switch. Battle Boats and Matchy Match; other games scan their existing legal moves or columns
- Armed direction — one switch drives two directions by making release meaningful: press moves the armed way, letting go arms the other, so a quick tap swaps sides. Race Tracks' one-switch steering
Genres already proven here
- Turn-based and puzzle — matching, memory, sequencing, sorting, word building, trivia, logic, board games. The easiest to adapt and still the backbone of the hub
- Sports — Baseball and Football play a whole season through play-calling menus, with a hold-to-charge on top for the swing, the throw and the kick; Football makes that charge optional with
Easy Throw. Bowling, Mini Golf and Basketball use oscillate-and-stop - Racing and action — Race Tracks steers with one switch and pairs a ten-level ladder with a no-fail Cruise mode. Bug Blaster is turn-paced tower defence
- Cause and effect — one press, immediate audio-visual payoff, no strategy required. Mega Slot and Show n Sound. Genuinely valuable for players who are building the idea that their action causes a result, and often the right first game for someone new to switch access
- Creative and editor-driven — Trivia Master, Matchy Match, Mini Golf's course editor. Highest replay value of anything in the hub, because a caregiver can tune the content to the player
Genre vocabulary in use in games.json: Strategy, Sports, Arcade, Classic, Board, Educational, Memory, Interactive, Simulation, Action, Puzzle, Trivia, Casino, Racing. Reuse one of these where it fits — the hub's genre filter is built from this list.
What to actually avoid
Never, in any form: continuous multi-axis control (steering plus speed at once); precise pointing at small targets; dragging; anything that punishes a slow or accidental press; and any fail state the player cannot see coming.
Allowed, but never as the only route: reaction tests and timed windows, quick-time moments, timers, health or lives, and hold-to-charge. All of these exist in shipped games and all of them work — but only because a no-fail mode, a generous limit, or a no-hold alternative sits alongside them, chosen by a setting. The mechanic is not the problem; being the only way through is.
Feedback and State
- On focus change: Speak the item name and show a strong highlight
- On selection: Speak confirmation and show a brief glow or scale pulse
- On error or locked action: Speak a short helpful line (e.g., "Need the key for this door")
- Always provide Undo or Back in the pause/context menu — whichever way the player reaches it (holding Return, the on-screen Pause button, or a scannable Pause item)
Menus and Pause
- Pause must be reachable from anywhere in gameplay. Include Continue, Restart, Settings, Main Menu and Exit Game
- Offer at least two ways in. Holding Return is the baseline gesture, and it must always be paired with an on-screen Pause button that does the same thing, for mouse, touch and caregivers. A player who cannot sustain a hold has no way in through the gesture alone — see Direction of travel below
- Make the hold visible while it happens — a filling ring and a rising beep, as Race Tracks does. A long hold with no feedback reads as a broken control
- Hold Return should also back out of sub-rows to row-select mode
- Settings must be reachable from the pause menu as well as the main menu — a scan speed that turns out to be too fast has to be fixable without abandoning the round
- Exit Game must tell the hub:
window.parent.postMessage({ action: 'focusBackButton' }, '*'). Without it the player finishes your game and is stranded with focus on a dead screen
Where the hub is today: nearly every game reaches Pause by holding Return, and most also put an on-screen Pause button on the HUD. That button is for mouse, touch and caregivers — in most games it is not in the scan cycle, so a switch-only player still has the hold as their single route in.
Direction of travel: holding a switch is itself an ability, and some players cannot sustain one. For them the hold gesture is not a convenient route to Pause, it is a locked door. So Pause is being made a scannable item — something the player can scan to and select — with the hold kept as a shortcut rather than the only way in.
This is rolling out gradually, newest games first, rather than as a sweep through all of them. Benny's Dice already does it (before the first roll, Roll and Pause are the only two scannable controls) and so does NARBE Animal Friends. The rest still rely on the hold. Favour a scannable pause entry in anything new; do not go retrofitting the existing games without asking, because the trade below is real.
But know what it adds. A scannable control is another stop on the scan cycle, and the player meets it on every pass, all session. If firing is the only in-play action, the player can settle in and play; add a scannable Pause and every shot now means waiting through Pause to reach Fire — and a player who never opens the pause menu is still waiting on it. A hold keeps play quick and direct for the players who can manage one and shuts out the players who cannot; a scannable control lets everyone in and puts a step between every player and the thing they came to do. Neither is simply better.
Where you can, keep the primary gameplay action off the scan cycle, put the scannable pause somewhere it does not sit between the player and the thing they came to do, or make it a setting — so the players who need it have it, and the players who do not are not left waiting on it.
Warn before a one-way door
A mis-timed Enter is the normal failure mode of switch access, not a rare accident. If a mis-scan opens a screen the player cannot operate or exit — an editor, an external tool, a file picker, anything that leaves the site — they are stranded until somebody walks past. Anything mouse-only must sit behind a confirmation.
The editors are the main case: they need a mouse and keyboard and are not switch-operable. Every game that has one already guards it — Trivia Master, Mini Golf, Show n Sound, Matchy Match, Word Jumble and P3GL. Match that pattern:
- Confirm first. Never let a single select open a mouse-only screen
- Cancel before Continue in the scan order — if the player mis-scans again inside the warning, the accident should land on the way out
- Trap the scan inside the dialog — while it is open the scan list contains only its own buttons. A warning you can scan straight past is not a warning
- Speak it the moment the dialog opens — not when the player scans onto an option. Many players do not read, and for them an unspoken warning is not a weak warning, it is no warning at all: they see a screen change they cannot interpret and press their switch again. Speaking only the option labels is not enough; "Continue" and "Cancel" say there is a choice but not what makes one of them dangerous
- Say what will happen in plain words — "you will not be able to scan and select with your switch" beats "this feature is advanced". Name the thing they lose, which is scanning
Settings to Include
Every game uses the same list, in this order. The consistency is part of the accessibility — a player who learns one settings screen has learned all of them.
- Text to Speech — On / Off
- Voice — cycles the available voices
- Game-specific options — difficulty, theme, ball style, direction help, and any timing or hold toggle the game owes (Football's
Easy Throw) - Auto Scan — labelled
On — One Switch/Off — Two Switches, not just On/Off. It is the control-scheme selector, and the person changing it is often a caregiver setting the game up for someone else - Scan Speed — 1s / 2s / 3s / 4s / 5s (default 2s)
- Sound Effects — On / Off
- Reset Progress — must be two-step; a single mis-scan should never erase everything
- ← Back
Auto Scan, Scan Speed, TTS and Voice are global — read and write them through NarbeScanManager and NarbeVoiceManager so a player configures their access once for the whole hub, not once per game. Game-specific options are yours to store.
Data and Persistence
- Auto-save progress on each selection that changes state
- Resume puts the player back in the exact frame with the previous highlight restored
- Access settings are global via the shared managers; game progress is the game's own
Graphics Quality for New Games
Every new game must assess rendering cost. Every Three.js or other graphics-intensive game must inherit the Hub's device-local Graphics quality preference before creating its renderer. Auto measures frame timing and chooses detail; High keeps full detail; Standard preserves the game's existing rendering and automatic adaptation; Low applies meaningful visual reductions without changing rules, physics, timing, inputs or random outcomes. Inexpensive games should document why a separate profile is unnecessary.
Load shared/platform.js, then shared/graphics-quality.js. Use a stable game ID; no registry or whitelist is needed. Add Use Hub setting / Auto / High / Standard / Low to the existing settings scan with the stable ID graphics-quality. Render the live label and announce it through the existing owned speech ticket. Preserve the selected item when values redraw.
const quality = NarbeGraphics.openGame('my-new-game');
await quality.ready;
const renderer = new THREE.WebGLRenderer(
quality.rendererOptions({ antialias: true })
);
function apply() {
renderer.setPixelRatio(quality.pixelRatio(originalRatio, width, height));
renderer.setSize(width, height);
quality.shadows(renderer, scene, originalShadows);
refreshSettingById('graphics-quality', quality.label());
}
const stop = quality.subscribe(apply);
apply(); // repeat on resize and adaptive resolution changes
// Once per rendered frame, after any intentional limiter:
quality.frame(timestamp, targetFPS); // e.g. 60 in play, 30 in a capped menu
// Run an older game-specific adaptation only in mode === standard.
// Existing settings action: quality.cycle(); announce(quality.label());
// Game teardown: stop(); renderer.dispose(); quality.dispose();
The refresh/announce functions above stand for your game's existing adapter. Keep HTML text and controls at normal resolution; preserve aspect ratio and input coordinates. The Low Three.js cap is 921,600 internal pixels and pixel ratio at most 1. Add appropriate per-game shadow/post-processing/decorative reductions. Avoid lowering canvas text resolution in 2D games. Dispose replaced render targets and stage subscriptions. A renderer option that cannot change safely in place takes effect on reopen; quality.label() indicates full change next launch.
The shared manager owns inheritance, overrides, migration and local persistence through NarbePlatform. Browser frames/tabs receive local notifications; native adapters must expose asynchronous readiness and native window notifications. Desktop uses native key-scoped persistence, asynchronous readiness and window/frame notifications. The source repository's GRAPHICS-QUALITY.md contains the complete reusable example, current profiles and Pi procedure; GRAPHICS-DESKTOP-PARITY.md describes the implemented native boundary. No Pi performance guarantee has been measured.
QA Checklist Before You Ship
- ✓ Can every action be performed with only Spacebar and Return?
- ✓ Can every action be performed with Return alone, with Auto Scan on?
- ✓ Can the same action be performed with mouse or touch, with no dragging anywhere?
- ✓ Does anything mouse-only or off-site sit behind a spoken confirm, with Cancel first and the scan trapped in the dialog?
- ✓ Do menu actions fire on release, not on press?
- ✓ Does holding Spacebar scan backwards in every menu?
- ✓ Does the native pause route remain available, with visible and audible feedback for supported holds? Document any gameplay lockout, such as Racer’s approved notice.
- ✓ Is there an on-screen Pause button that does the same?
- ✓ Are Settings reachable from both the main menu and the pause menu?
- ✓ Do Auto Scan and Scan Speed read from
NarbeScanManager, with no hard-coded interval? - ✓ Does TTS read focus, selection, errors, and outcomes via
NarbeVoiceManager? - ✓ Is all sound through
SafeAudio, with noAudioContext? - ✓ Is Reset Progress two-step?
- ✓ Does Exit Game send
postMessage({ action: 'focusBackButton' })? - ✓ Does help text describe holds without quoting a number of seconds?
- ✓ Is there nothing that demands precise pointing, a drag, or two axes at once?
- ✓ Have timed and hold mechanics been preserved, with any proposed tap-friendly alternatives recorded for a separate optional gameplay pass? Existing hold mechanics do not fail the scan-upgrade checklist.
- ✓ Is it readable at 100% on a tablet?
- ✓ Does the game auto-save and resume correctly?
- ✓ Does the rendering assessment explain the graphics profile or why none is needed?
- ✓ Does a graphics-intensive game inherit Hub quality before its first frame, follow live updates, respect overrides, reset to inheritance and persist after reload/restart?
- ✓ Do Auto/High/Standard/Low, old preference migration, resize/split views, aiming/picking, readable text, stable settings IDs and native gameplay/pause/Back/exit pass real browser checks?
- ✓ Are replaced rendering resources/subscriptions disposed, and are target-device frame times documented or explicitly pending before hardware support is claimed?
- ✓ Is it listed in
games.jsonwith a thumbnail and genres? - ✓ Has someone who is not you played it start to finish with one switch?
Example: P3GL Input Setup
P3GL is worth studying because the same game presents a different aiming model in each control scheme — and that is what lets one build serve both a two-switch player who wants control and a one-switch player who cannot hold a switch at all.
Before every shot: Take shot or Options
Each shot starts with a two-item choice. Spacebar moves between Take shot and Options, and releasing Return chooses. Options opens pause and settings, so every player has a way out without holding a switch. Take shot starts aiming without firing.
Two switches (Auto Scan off)
The player drives the aimer by holding. It does not sweep on its own.
- Spacebar (hold): the aimer sweeps while the switch is down, and stops the instant it is released. A brief press nudges it a little; a long hold moves it far
- Each new press reverses direction, so the player walks the aimer onto the target — over, back, in — instead of chasing a moving marker
- The aimer also turns around by itself at the edges of its arc, so it can never be driven off the end
- Return (press and release): fires
- Return (press during a shot): pauses
One switch (Auto Scan on)
Auto Scan cycles Take shot and Options on its own. After Take shot, the aimer oscillates continuously and Spacebar is not needed for aiming at all.
- Return (press): stops the aimer; releasing fires at that angle — the only input the game requires
- Options, scanned before every shot, opens pause and settings
Aim speed
Four presets — Super slow, Slow, Steady, Quick — defaulting to Super slow. This is the difficulty dial for the whole game, and defaulting to the slowest setting is deliberate: it makes the game winnable first, and a player who wants more challenge can speed it up.
Two things to take from this. The same mechanic can be expressed as "hold to move" or "press to commit" depending on the player's scheme — build both rather than picking one. And the speed of anything that moves on its own should be a setting, defaulted to the accessible end.
P3GL used to be hold-first: holding Return opened the pause menu. That kept play to the fewest possible switch actions, but it left the way out behind a sustained hold that some players cannot manage. It now puts Take shot and Options before every shot. That costs one selection per shot, and in return every player reaches pause and settings with the same scan and select they use everywhere else. A long hold of Return still backs out of aiming, but only as a convenience; nothing depends on it.
Have questions or want to contribute? Reach out to us.