Skip to content
agdesteinPublic

Repository files navigation

CWI Science Arcade

An arcade of small, playable simulation games for the CWI Science Day, showcasing the Scientific Computing group.

Kids walk up to a stand, pick a game on the screen, and play with a real-time simulation — stirring turbulence, building dikes against a storm surge, stopping an epidemic, teaching a creature to walk. Every game is powered by the same kind of mathematics our group works on every day.

The event

  • What: CWI Science Day — kids (with parents) visit the institute; every research group has its own stand.
  • When: Saturday 3 October 2026.
  • Our stand: one to three computers (keyboard, mouse, monitor) running this app.
  • Visitors: spend anywhere from 30 seconds to 15 minutes per stand. Kids are the primary audience; parents are an important secondary audience.

What we want to communicate

Scientific computing can simulate anything. The same mathematics that predicts the weather also designs wind farms, fights epidemics, protects the Netherlands from the sea, and teaches AI to move. We are the group that makes computers do that.

See docs/message.md for the full formulation of our goals and the per-audience message, and docs/ideas.md for the catalog of game ideas.

The app

One self-contained web app (the "arcade"): a launcher menu where each game is a tile. Games are independent modules sharing a small common shell (fullscreen kiosk mode, optional idle-reset back to the menu, local high scores). Runs offline in a browser on any laptop — nothing to break on the day.

The menu carries the booklet's title (De simulatie-arcade – kun jij de wereld nabootsen?) and the stand's one-line message. Each tile says in a few words what you do there and shows today's record on that machine (or the computer's, 🤖, to beat); the science sentence for parents is on each game's title card. A QR code at the top left opens the same arcade on GitHub Pages, for playing again at home.

The menu's "🔬 How does this work?" button opens a card layer with the big picture — how nature becomes equations, what scientific computing is, why computers were built (the Mathematisch Centrum built the Netherlands' first), who CWI and our group are, and the one recipe every game follows. The same button inside each game opens that game's own science explainer.

Planned first games (see docs/plan.md for the full implementation plan):

  1. Fluid playground / wind farm challenge — real-time 2D flow you can stir; place turbines, harvest power, beat the daily record.
  2. Outbreak! — stop a simulated epidemic in a mini-city (our group has actually worked on epidemic simulation). An agent-based SIRD model with ages and a hospital; the challenge scores lives saved against simulated do-nothing futures.
  3. Save the Netherlands — hold back a live storm surge with sand; dikes that overtop wear away and breach.
  4. Creature Lab — a park of stick creatures to grab and fling; teach one to walk by evolution in a simulated world (an hour of practice a minute) and race it for today's crown.
  5. Weather Detective — a real KNMI weather map hides under the fog; place thermometers and a Gaussian process guesses the rest, shows how sure it is, and "dreams" possible weathers. Three cases: find the hottest place, make the weather map for the news, catch the lying home weather stations.

Running the app

Requires Node.js.

cd app
npm install
npm run dev       # development server with live reload
npm run build     # static production build in app/dist
npm run preview   # serve the production build

At the stand, after npm run build, one command serves the build on the machine itself (no network) and keeps Chrome, Chromium or Edge open on it in kiosk mode, in Dutch, with the idle reset at 180 s:

node tools/kiosk/kiosk.mjs                              # or: cd app && npm run kiosk
node tools/kiosk/kiosk.mjs --games=windfarm,bounce --reset-scores
node tools/kiosk/kiosk.mjs --windowed                   # a normal window, to test

The stand's paper (the staff's stand sheet in English and Dutch, the kids' stamp cards, and the "play at home" QR poster) prints from tools/print/ to promo/print/*.pdf with node tools/promo/cdp.mjs ../print/print.mjs.

On Windows, double-click tools/kiosk/kiosk.bat (or drag it into a terminal with options). Every --name=value other than the launcher's own (--port, --browser, --windowed, --serve-only, --dry-run) becomes one of the URL options below. The browser gets its own profile (~/.cwi-arcade-kiosk), so the day's scores survive a restart. If a visitor closes the browser it reopens after two seconds; staff close it twice within ten seconds (Alt+F4, Alt+F4) to quit. Opening app/dist/index.html straight from disk does not work (browsers block module scripts from file://).

Useful at the stand:

  • Add ?games=bounce,orbits (comma-separated game ids) to the URL to limit which games a machine shows, so each computer showcases a different subset.
  • Add ?lang=nl (or en, no) to set a machine's default language; visitors can switch with the flag buttons on the menu. English is the development/source language, Dutch is the event language, Norwegian exists for playtesting. Translations live next to the code that uses them as typed Localized<...> dictionaries (see app/src/lib/i18n.ts) — a missing translation is a compile error, so the three languages cannot silently drift apart.
  • Add ?home=windfarm (any game id) to make a machine rest on that game instead of the menu: it opens there, running behind its see-through title card, and goes back to it after an idle reset (a home game nobody touched reloads quietly every idle period, restarting its opening animation). Give each machine a different home, so the stand shows several live simulations from a distance; ⌂ still leads to the menu with all the games. Swirl Lab stirs itself (a "ghost hand") whenever it is left alone for a few seconds.
  • Add ?quality=low on a slow machine to run Swirl Lab's fluid solver on its coarse grid from the start (?quality=high pins the fine grid). By default it starts fine and drops to coarse by itself if the first seconds stutter. The same switch runs Save the Netherlands' free play on the cheaper first-order scheme (it also drops to it by itself when the solver is slow).
  • Outbreak!'s free play has nobody dying by default; add ?deaths to use the full model there too (the challenge always has it). See the remake notes.
  • Sound: the 🔊 button on the menu mutes the whole arcade (remembered on that machine until the next idle reset); ?sound=off starts a machine muted. All sounds are synthesized, no files.
  • Press F (or the corner button) for fullscreen; Esc returns to the menu (it also rescues a game that went black or froze).
  • Add ?idle to reset a game after 90 seconds without input (?idle=180 for a different number of seconds): a "Still playing?" countdown shows for the last 10 seconds, then the page reloads to the menu in the machine's own language and sound setting (a visitor's 🇳🇴 or 🔇 is forgotten). Off by default, so nobody reading a science explainer at home gets kicked out mid-sentence.
  • Staff: Ctrl+Shift+Backspace clears all of today's scoreboards and Creature Lab crowns (after the morning's test runs); ?reset-scores in the URL does the same on load. Rude initials (and CPU, the computer's name) are refused.
  • Canvases render at most about 1080p worth of pixels, so a 4K big screen costs no more than a 1080p one; ?dpr=1 (or any ratio) pins the resolution on a machine.
  • The arcade blocks the browser's right-click menu and Ctrl+wheel zoom, asks the screen not to sleep, and goes back to the menu by itself if a game keeps failing or loses its WebGL context.

Credits

The real-time fluid solver behind the wind-farm game (app/src/games/windfarm/) is adapted from WebGL-Fluid-Simulation by Pavel Dobryakov (MIT License), itself based on the stable-fluids method of GPU Gems ch. 38. Our version is a TypeScript/WebGL2 rewrite of the solver core, with added wind inflow from any direction (the wind turns during a round), open outflow edges, obstacles, yawing turbine drag disks, velocity probes, the wake view with GPU tracer particles, and an engineering wake model for the computer player.

Creature Lab (app/src/games/creature/) is written from scratch (a small Verlet stick physics in the style of Jakobsen's Advanced Character Physics plus a simple genetic algorithm), but its design — build a creature from nodes, bones and oscillating muscles, then watch evolution teach it to walk — follows a beloved lineage: Karl Sims' Evolved Virtual Creatures (1994), carykh's Evolution Simulator videos, and Keiwan Donyagard's Evolution. It opens on a park of creatures walking on trained brains, to grab, fling and startle. Teaching one shows a practice clock (simulated time, tries), lets a kid pick parents by hand, and has turbo and four rewards (far, race-walk, jump, backwards); the race is against today's champion of the same kind of body (crowns per kind and day, with initials). Muscles have a speed limit: without it evolution learned cartwheels and 5 m worm jumps, which the delve's 🐞 switch brings back. The challenge has three rounds: Pick the parents (you are the selection, then the computer does 300 picks), Race-walk (choose the reward; a judge counts only distance with a foot on the ground) and Into the wild (practise on a flat floor or changing bumps, then cross an unseen course). Brains can also feel (senses and a tiny neural network adding reflexes to the beat), practice can come with shoves, the park's ⛰️ Design asks for a body that crosses bumps with flat practice only, and the computer can take its own turn at the challenge. The pre-trained brains are generated by npm run train:creature (tools/creature/train.ts) and must be re-trained when the physics changes. See the proposal (npm run test:creature).

Save the Netherlands (app/src/games/floodland/) runs a finite-volume shallow-water model (hydrostatic reconstruction, Rusanov fluxes, wetting and drying) live every frame, with erosion so overtopped dikes breach. Free play is a water table instead of a dike: a spring feeds a river through a pond to a beach where waves roll in, and you push and pour water, pile up sand (dams burst, the big wave takes islands), dig new rivers and drop rubber ducks, on a second-order version of the same solver; the challenge has four rounds (hold the line, find the weak spots with test storms, choose a dike height against a century of random storms after van Dantzig's 1956 analysis, and close a storm-surge gate on an ensemble forecast). See the notes.

Ball Pit (app/src/games/bounce/) is a position-based disc simulation (substeps, a neighbour grid, restitution and friction) you can shove, pour, boil into a gas, cool again and zoom out of until it looks like a fluid. Its challenge has three rounds: Plinko (one ball is luck, three hundred make a bell curve), Silo (jams come at random; knock them loose) and Steam engine (hot balls lift a heavy lid). It is meant as the arcade's microscope: everything the other games simulate as a smooth fluid is made of balls like these, and its delve goes from one ball to why we write equations for the crowd. See the proposal (npm run test:bounce).

Gravity Doodle (app/src/games/orbits/) is an N-body toy: fling pebbles, planets, giants or a second star around the Sun, and every body pulls on every other; bodies that touch merge. While you aim, the 🔮 forecast runs a copy of the same simulation ahead (fixed steps, so it is exactly what will happen). Two lenses: a rubber-sheet gravity view, and a step slider that makes the computer's time steps huge and swaps smart (symplectic) steps for Euler's, which spiral out. Its challenge has three rounds: Goldilocks (keep planets in the water zone; life grows on calm ones, crashes melt it), Slingshot (reach the golden ring by a flyby of the giant) and Save the Earth (an asteroid nobody knows exactly is a cloud of possible asteroids; look to shrink it, then push once, early). Presets show our inner solar system to scale, a two-sun system and the three-star figure-8; in Slingshot the computer can fly one probe a round for you (watch it try 192 routes), and after a challenge it can take its own turn and post a CPU score. The delve ends in chaos twins and clouds of possible futures. See the proposal (npm run test:orbits).

Weather Detective (app/src/games/detective/) grew out of our group's project with KNMI on multi-fidelity Gaussian process regression of crowdsourced (home) and official weather stations, and out of last year's Science Day game about that project, CWI_gp_temp_scienceday by Rik Hoekstra. Its temperature maps are KNMI's high-resolution weather maps of four days in 2025 (from that game), adapted for play; the Europe dot map in its explainer shows the official ECA&D stations (public) and home stations made up from them. tools/detective/prep.jl turns them into the game's data file. See the notes for the model, the cases and their calibration (npm run test:detective).

Repository layout

  • README.md — this file, the entry point.
  • docs/message.md — goals: what the stand should achieve and communicate.
  • docs/ideas.md — catalog of game ideas (built, planned, and future).
  • docs/plan.md — architecture and phased implementation plan.
  • docs/arcade-assessment.md — assessment of the arcade as a whole (menu, shared controls, pacing, science thread, Dutch, stand readiness) and the plan for the last week before the event.
  • tools/ — scripts that are not shipped: data preparation, promo stills, printables (tools/print/) and the stand launcher (tools/kiosk/).
  • app/ — the arcade web app (Vite + TypeScript; games live in app/src/games/).

Contributors

Languages