Rad Cohesion #
A browser puzzle about packing triangular pieces onto a polyhedron. Fill an original face to clear it; pieces can cross face boundaries and wrap around corners. Play against the clock or build layouts in Free Play, then share scenarios and submit scores.
Run locally #
Use Node.js 24.16 or later in the Node 24 series and a browser with WebGL.
npm ci
npm run dev
Open http://127.0.0.1:5173/game/ and press Start. The website and APIs
run together; local play needs no account or external service. Use
?seed=1234 for a reproducible piece sequence. Use 127.0.0.1 rather than
localhost if signing in, so the game and OAuth callback share browser storage.
Play #
In Timed mode, place the current piece before its countdown expires. A legal piece locks automatically at timeout; an invalid timeout costs a strike. Three strikes end the run. A blocked manual placement leaves the piece and timer active. A jammed next piece ends the run only if no saved reward piece can rescue it.
Full faces clear simultaneously. Points are 100 × faces² × level, using the
level before the clear. Every five cumulative faces cleared advances a level.
Subsequent piece timers shorten from 10 seconds by one second per level, down
to four seconds. New game lets you choose a starting level from 1 to 99.
Restart repeats the seed and retains the starting layout and level.
| Input | Action |
|---|---|
| Hover | Aim the piece; mint means legal, red means blocked. |
| Click / Space | Place the piece. |
| R / Shift+R | Rotate clockwise / counterclockwise by 60°. |
| F | Reflect the piece. |
| Drag | Orbit in 3D or pan in 2D. |
| Scroll / pinch | Zoom. |
| 3D / 2D | Change the display without changing the game. |
| Recenter / C in 2D | Center the current aim and reset zoom. |
| Reset view | Center the current aim at default zoom. |
| P / Esc / Pause | Pause or resume; pausing hides the board. |
Touch uses center aim: drag the board beneath the preview, then tap to place. On-screen rotate, flip, and Place buttons replace keyboard controls. Desktop can enable Center aim too. Dragging never places or erases a triangle. On compact screens, scroll vertically to reach controls when needed.
Clearing multiple faces banks one saved piece: two faces earn a three-tile Triamond, three earn a two-tile Diamond, and four earn a one-tile Moniamond. Activating one suspends the normal piece and its timer while keeping the queue. Afterward, the normal piece resumes. Saved pieces stack; only one can be active at a time.
Boards and Free Play #
New game offers an icosahedron or octahedron and an edge subdivision k
from 1 to 10. Each original face contains k² cells: 20 × k² on an
icosahedron, 8 × k² on an octahedron. The default is an icosahedron at k=4
(320 cells). URLs can select these too: ?polyhedron=octahedron&k=4.
At k=1, each four-tile piece fills and clears four faces. At larger sizes,
Hook can wrap around an octahedron vertex to clear four faces; Ribbon and Crown
can touch at most three there. The size ceiling limits placement-index memory.
Free Play has no countdown, random queue, strikes, or game over. Select any piece or the eraser; full faces still clear. Clear board removes all cells. Changing the board shape or size creates an empty board, so save a scenario first if you want to keep the layout.
Audio settings has separate Music (Off, Track 1, Track 2) and Sound effects choices. Both start off and remember their settings. Audio starts after user interaction; music pauses with the game, hidden tab, or game over.
Save and share #
The header links to Play, Leaderboards, Scenarios, and Library.
- Save game names a resumable checkpoint. Library manages saved games, local scenarios, and finished runs. These stay in this browser profile and site origin; signing in does not upload or synchronize them.
- Leaving play preserves temporary recovery for the tab. Play and reload restore it paused. An explicit new game or scenario launch replaces it.
- Save scenario in Free Play stores a reusable layout. Sign in to publish a separate public copy. Public Scenarios supports search, previews, and play.
- Leaderboards lists layouts with submitted results and their populated starting levels. Exact standings offer Play this challenge.
- Submit score on Game over or in Library publishes to the signed-in account, or offers a Guest submission with an optional name and sign-in link. All current scores are unverified summaries.
Install and offline play #
The production app can be installed through the browser's install menu, or Safari's Share → Add to Home Screen. After the first online load finishes caching, the game, local libraries, and audio work offline. Public discovery, sign-in, and submissions need connectivity. Updates activate after existing app windows close.
For local production testing, run npm run build then npm start and open
http://127.0.0.1:3000/. Installation requires HTTPS or localhost; development
mode does not register the production service worker.
Development #
npm test # Core, persistence, rendering, and UI unit tests
npm run lint # Oxlint and vendored anti-slop rules
npm run typecheck # Svelte and browser/server TypeScript checks
npm run build # Typecheck and build the production Node app
npm run test:server # AppView, migrations, and HTTP API tests
npm run test:browser # Playwright interaction and navigation tests
npm run test:pwa # Production subpath, installability, and offline tests
npm run test:shutdown # Production server shutdown test; build first
Install Chromium once with npx playwright install chromium. Browser fixtures
expose test state; the production game does not. npm run test:all runs unit
tests, build, server tests, and shutdown checks; browser and PWA checks are
separate.
Documentation #
- Architecture: code boundaries, data flow, and lifecycle.
- Deployment: configuration, OAuth, ingestion, and backups.
- Roadmap: multiplayer preparation and deferred features.
For implementation contracts, see sessions and snapshots and records and APIs. Start with Architecture before those references.
- TypeScript 86%
- Svelte 13%
- CSS 1%
- Docker <1%
- HTML <1%