quill/pixel-plays
Public: anyone can read this; nobody can change it.
forked from trevato/pixel-plays · migrated from github.com/amirmushichge/pixel-plays
This repository has no app yet.
following trevato/pixel-plays in step
Everything here is up to date with trevato/pixel-plays.
Proposes updates from trevato/pixel-plays.
main
View all branches
- .claude
- allotment
- apple-bobbing
- archery
- balote
- beach-huts
- beekeeper
- bell-tower
- bird-watch
- bottle-bank
- bowls-green
- bread-oven
- bumper-cars
- bunting
- cake-stall
- campfire
- carousel
- carrot-pull
- cheese-rolling
- chestnut-roast
- chimney-sweep
- cloakroom
- clock-mender
- coconut-shy
- conkers
- curling
- dinghy-race
- domino-run
- duck-race
- egg-basket
- empire
- espresso-bar
- fence-posts
- ferris-wheel
- ferryman
- firefly-jar
- free-throw
- go-kart
- goalkeeper
- harbour-crane
- hay-loft
- hedge-maze
- hoopla
- hot-air-balloon
- ice-fishing
- jam-jars
- kite-hill
- laundry
- library-shelf
- lighthouse
- lily-pond
- lock-keeper
- market-barrow
- market-scale
- milk-churns
- mill-race
- mini-ramp
- og
- orchard
- pancake-griddle
- paper-route
- pigeon-post
- pizza-toss
- pottery-wheel
- pumpkin-patch
- quilt
- rain-barrel
- rock-pool
- row-home
- sack-race
- sandcastle
- scripts
- seed-packets
- sheepdog
- shunting-yard
- signal-box
- skipping-rope
- sled-run
- snail-race
- soapbox-derby
- stained-glass
- stari-most
- stone-skip
- stone-wall
- tea-party
- ticket-booth
- tide-crossing
- tie-break
- tightrope
- tower-duel
- tram-line
- tug-of-war
- village-darts
- village-draughts
- welly-toss
- window-washer
- wood-split
- .bash_profile 0 B · ~0 tok
- .bashrc 0 B · ~0 tok
- .gitconfig 0 B · ~0 tok
- .gitignore 29 B · ~9 tok
- .gitmodules 0 B · ~0 tok
- .idea 0 B · ~0 tok
- .mcp.json 0 B · ~0 tok
- .profile 0 B · ~0 tok
- .ripgreprc 0 B · ~0 tok
- .vscode 0 B · ~0 tok
- .zprofile 0 B · ~0 tok
- .zshrc 0 B · ~0 tok
- engine.js 16.3 KB · ~4.8k tok
- forge.json 89 B · ~30 tok
- index.html 28.8 KB · ~9.8k tok
- LICENSE 1.0 KB · ~306 tok
- limits.mjs 791 B · ~226 tok
- limits.test.mjs 681 B · ~195 tok
- MUSIC.md 383 B · ~96 tok
- playtest.mjs 8.9 KB · ~2.6k tok
- playtest.test.mjs 5.6 KB · ~1.6k tok
- plaza.js 8.4 KB · ~2.4k tok
- plaza.test.mjs 5.6 KB · ~1.6k tok
- ramp.js 1.4 KB · ~409 tok
- ramp.test.mjs 1.2 KB · ~352 tok
- README.md 62.0 KB · ~16k tok
- rounds.mjs 40.4 KB · ~12k tok
- serve.mjs 4.1 KB · ~1.2k tok
- serve.test.mjs 36.7 KB · ~11k tok
- shell.js 29.7 KB · ~8.7k tok
- shell.test.mjs 9.6 KB · ~2.8k tok
- stats.mjs 4.0 KB · ~1.2k tok
- stats.test.mjs 2.4 KB · ~716 tok
- town.js 4.9 KB · ~1.4k tok
- town.test.mjs 6.0 KB · ~1.8k tok
- vercel.json 28 B · ~10 tok
README.md
Pixel Plays
Small browser games shown as isometric pixel dioramas. Every game is one page of plain JavaScript with its own software rasteriser: no build step, no dependencies, no sprites or image assets.
Play: https://pixel-plays-ruby.vercel.app/
Concept by abhi. Game by AmirMushich. Visual reference for the style: shawn.'s Small plays; no assets or subjects were copied.
Games
| Game | What you do | Input |
|---|---|---|
| Balote | Bocce against a bot, first to 5 | Drag back and release; arrows + space |
| Paper Route | Ten newspapers thrown from a passing bike: porch 3, lawn 1, a window costs 2 (forged by the crew on the engine and shell above, not by the original creators) | Tap; space |
| Stari Most | Five dives off the old bridge, flip and enter the water vertically | Hold and release |
| Rooftop Laundry | Get the washing into the basket before the storm | Drag; space |
| Tie-Break | Clay-court tie-break against a bot, the moment you hit decides where the ball lands | Tap; space |
| Mini Ramp | 60 seconds on a skateboard ramp, pump for height and grab in the air | Hold and release; space |
| Kite Hill | Keep a kite in the air through 60 seconds of gusts: pull to climb, let out line when the grass bends (forged by the crew on the engine and shell above, not by the original creators) | Hold; space |
| Stone Skip | Skim a flat stone across a lake, five throws, count the bounces (forged by the crew on the engine and shell above, not by the original creators) | Drag back and release; arrows + space |
| Pizza Toss | Five tosses of pizza dough: hold to spin, catch it before it drops, too much spin tears it (forged by the crew on the engine and shell above, not by the original creators) | Hold and release, tap to catch; space |
| Harbour Crane | Stack ten crates onto a bobbing fishing boat from a swinging crane hook; one hanging more than half off tips into the water (forged by the crew on the engine and shell above, not by the original creators) | Tap; space |
| Sled Run | Ride a sledge down a snowy hill for 40 seconds: tuck for speed, release on a kicker to jump, land flat for points and nose-down to tumble (forged by the crew on the engine and shell above, not by the original creators) | Hold and release; space |
| Free Throw | Ten shots on a rooftop court from five spots that step back from the hoop: hold to crouch and build power, release to jump, tap at the top of the jump to shoot; a swish scores 3, off the rim or backboard 2, and the fig leaves show the wind that nudges long shots (forged by the crew on the engine and shell above, not by the original creators) | Hold and release, tap to shoot; space |
| Pottery Wheel | Three pots on a kick wheel: hold to centre the clay without pressing too hard, then pull the wall up, too fast tears it and too slow slumps, and each pot has 40 seconds (clay still being centred then dries and is lost, a pull is fired as it stands); the score is the total height in pixels (forged by the crew on the engine and shell above, not by the original creators) | Hold to press, drag up to pull; space, up arrow, F to fire |
| Row Home | Race a rowing boat across the river against a bot: stroke in rhythm with the glide, too early catches a crab, hold left or right to steer against the current (forged by the crew on the engine and shell above, not by the original creators) | Tap or space to stroke; hold left/right |
| Orchard | Catch falling apples in a basket: ripe 1, golden 3, a wasp-bitten brown one costs a life, three lives end the round (forged by the crew on the engine and shell above, not by the original creators) | Drag; left/right arrows |
| Lighthouse | A 90-second night: sweep the lighthouse beam so boats drifting in towards the rocks are held in the light for a second and turn away, +1 each; three boats lost on the rocks end the night early (forged by the crew on the engine and shell above, not by the original creators) | Drag; hold left/right |
| Window Washer | Five grimy windows, twelve seconds each: wipe the glass clean with a squeegee before the cradle drops a floor, bird droppings need two passes, score the percentage clean (forged by the crew on the engine and shell above, not by the original creators) | Drag; arrows + space |
| Market Scale | Five customers, 15 seconds each: weigh fruit onto a hanging pan scale to match the weight on the chalk slate, exact scores 3 and within 50 g scores 1 (forged by the crew on the engine and shell above, not by the original creators) | Drag or tap fruit, tap the bell; arrows + enter, space |
| Domino Run | Twenty dominoes to stand along a curving chalk path in three courtyards, then tip the first: a gap too wide stops the chain, too close topples one early; ringing the bell scores 10 and each domino left over 1 more (forged by the crew on the engine and shell above, not by the original creators) | Tap the path, or arrows + space; tip first button or enter |
| Beekeeper | A 60-second hive inspection: hold the smoker to calm the bees (its fuel runs low and refills slowly), tap a frame to lift it into the crate, golden frames score 3 and half-capped 1, a dark brood frame costs a sting; three stings or a swarm end the round (forged by the crew on the engine and shell above, not by the original creators) | Tap a frame, hold the smoke button; arrows + space, hold S |
| Campfire | A 90-second night: keep a campfire at a good height by dragging twigs, branches and logs into the ring; a log smothers a small fire and rain damps the flames (forged by the crew on the engine and shell above, not by the original creators) | Drag from the pile; arrows or 1/2/3 + space |
| Bread Oven | A 60-second shift at a brick oven with four slots: slide dough in with the peel, crusts go pale, golden, dark, burnt, and slots nearer the fire bake faster; pulling golden scores 3, dark 1, pale 0, and a burnt loaf is a strike, three end the round (forged by the crew on the engine and shell above, not by the original creators) | Tap a slot, or arrows + space to slide in, enter to pull |
| Tightrope | Three crossings of a wire between two rooftops: the walker steps on alone while a lean angle falls away from centre and wind gusts (shown in the grass and the chimney flag a beat early) tip it; hold left or right to lean back, but a long hold overcorrects, and past 30 degrees you drop to the net and restart from the last of three marks; a crossing scores 10 plus 1 per second under 60, and 5 more if the lean never passed half the limit (forged by the crew on the engine and shell above, not by the original creators) | Hold left or right half of the canvas; arrows or A/D |
| Wood Split | A 60-second round at a chopping block in a farmyard: hold to raise the axe (the longer the hold the stronger the swing, up to a peak, then your arms tire) while the blade sways across the log top, release to swing; on the grain crack at full strength splits it clean for 3, near it for 1, off it or too weak sticks the axe and costs two seconds, and knotty logs need full strength; split halves stack on the woodpile (forged by the crew on the engine and shell above, not by the original creators) | Press and hold the canvas; hold space |
| Bell Tower | The tower rings a peal on four bells of different sizes and you ring it back by pulling the ropes in the same order; a bell must swing back before it rings again, a right peal scores its length and the next is the same peal plus one bell (it starts at three), and a wrong bell ends that peal without scoring, three wrong peals end the game; each bell flashes as it swings, so it plays muted too (forged by the crew on the engine and shell above, not by the original creators) | Tap a rope; 1-4 or the arrows, one per bell |
| Signal Box | A train waits at a country junction and the signalman's lamp lights a sequence of levers, one by one, on a row of five; you pull the same levers in the same order. Right and the train runs through on the route the points set; a wrong lever jams the points and that train is lost. Each routed train adds one lever to the next sequence (it starts at three), three lost trains end the game and eight routed is a win; pulls are ignored while the lamp is lit, and each lever is lit for over half a second so it is memory, not a guess; lit levers show without animation under reduced motion, so it plays muted too (forged by the crew on the engine and shell above, not by the original creators) | Tap a lever; 1-5, or the arrows and enter |
| Ice Fishing | A 60-second round on a frozen lake: hold to let line out through the hole and watch the hook sink in the cut-away, a perch (1), trout (2) or pike (3) crossing the hook at its depth bites, then hold to reel and ease off to let the tension fall; full tension snaps the line and three snaps end the round (forged by the crew on the engine and shell above, not by the original creators) | Hold the canvas; hold space |
| Archery | Ten arrows at a straw target across a meadow: drag back from the archer to draw (the drag length is power, its angle is aim), release to shoot; the wind changes every arrow and shows on the pennant and the grass, holding past three seconds makes the aim sway wider, and rings score 10 (gold), 8, 6, 4 and 2 (forged by the crew on the engine and shell above, not by the original creators) | Drag back, release; arrows + hold space |
| Tram Line | A 120-second timetable over eight stops: hold to power, release to coast, brake so the front door lands on the yellow mark; within 0.5 m scores 3, within 2 m scores 1, overshooting the shelter scores 0 and strands the passengers, stopping between stops costs 1, and each second left at the last stop adds 1 (forged by the crew on the engine and shell above, not by the original creators) | Hold up or W (right half) to power, down or S (left half) to brake |
| Espresso Bar | Twenty cups slide past under an espresso spout, each with a painted fill line and an order card (espresso a short line, latte a tall one): hold to pour, and the stream keeps falling for a quarter second after you let go; within 3% of the line scores 3, within 10% scores 1, over the brim spills and costs a life, and a cup that passes untouched costs a life; cups come faster as the round goes, three lives end it (forged by the crew on the engine and shell above, not by the original creators) | Hold space, or press and hold the canvas |
| Goalkeeper | Ten penalties in a village football ground: each taker runs up and their hips and plant foot lean toward the side they will shoot (arms spread wide is down the middle), readable for a short tell window before the kick that shrinks with every penalty; tap the left third of the canvas to dive left, the right third to dive right and the middle third to stay up, and a dive to the right side before the ball arrives saves it, while diving before the tell window opens lets the taker switch side half the time; seven saves wins; the concept is the ordinary football penalty, with no particular game as its source (forged by the crew on the engine and shell above, not by the original creators) | Tap left, middle or right; Left and Right arrows dive, Up stays |
| Soapbox Derby | A 360 m cobbled hill in a wooden cart that rolls and gathers speed by itself: steer between the gaps in gates of hay bales that narrow down the hill (the cart answers late, so steer early); a bale slows it hard and costs one of three lives, a smooth flagstone strip gives a short boost, and the banner scores your time against your best (forged by the crew on the engine and shell above, not by the original creators) | Drag left or right; left/right arrows |
| Tug of War | A village fete: two teams of four on a rope, a ribbon tied at its middle and a chalk line at each end. A caller beats a steady heave shown as a pulsing marker; a tap within 150 ms of the beat pulls the ribbon towards your line, a tap well off it (over 300 ms) makes your team stumble and the rope slips the other way, and not tapping lets the rival pull steadily, stronger each round. The ribbon crossing your line wins the pull, crossing theirs loses it; five pulls, three won is a win. The marker shows as a lit dot without animation under reduced motion, so it plays muted too (forged by the crew on the engine and shell above, not by the original creators; rules in tug-of-war/rope.js) | Tap anywhere on the picture; space |
| Sheepdog | A 90-second dusk over three hillside fields, five sheep each, scattered at the top: drag the dog (or use the arrow keys) so they flee it inside a set radius, flock together and wander when calm, and push one too hard and it bolts the wrong way; each sheep that goes through the dry-stone wall's gate and then into the hurdle pen scores, one that goes round the gate scores nothing once penned; the gate moves every field, the sun lowers with the clock, and 10 or more of 15 wins (forged by the crew on the engine and shell above, not by the original creators) | Drag the dog; arrow keys |
| Firefly Jar | A 90-second summer night in a back garden: tap a firefly while it glows to catch it into the jar (+1, golden +3), a dim one is scared off and breaks the streak, shoo the moths before they reach the lantern; the third moth ends the night (forged by the crew on the engine and shell above, not by the original creators) | Tap; Tab or arrows + space |
| Egg Basket | A red henhouse with a sloping tin roof and three hens on the ridge: they lay brown eggs (1 point) and rare golden ones (3) that roll down one of four roof lanes and drop off the eave; slide the basket under each before it reaches the ground, where it cracks; rolling speeds up every ten eggs laid (never so fast that the basket cannot cross three lanes in time), three cracks end the round and 25 or more wins (forged by the crew on the engine and shell above, not by the original creators) | Drag the basket; Left and Right arrows move one lane |
| Coconut Shy | A village-fete stall with bunting and a stallholder, five coconuts on wooden posts: drag back and release to throw (or Left and Right to aim, hold Space for power, release to throw); a ball inside the square window knocks a coconut off its post, two glancing hits knock it too, and clearing all five wins a bonus ball; five balls a round, three rounds with the posts moved further back each time, and nine down or more of the game wins (forged by the crew on the engine and shell above, not by the original creators; rules in coconut-shy/shy.js) | Drag back and release; Left and Right to aim, hold Space |
| Hoopla Stall | A fairground stall with nine prize blocks on a three-step table under a striped awning and eight wooden rings: drag back and release to toss (or Left and Right to aim, hold Space to build power, release to toss); a ring that settles over a block scores its row, 1, 2 or 3 with further back worth more, and takes the prize; a ring that touches a block's edge bounces off, and 12 points or more wins (forged by the crew on the engine and shell above, not by the original creators) | Drag back and release; arrows + space |
| Bowls Green | A flat lawn bowls green floating on white, a white jack at the far end, a mat at the near end, two rival woods already resting near the jack and a low hedge behind: drag back from the mat and release to roll (or Left and Right to set the line, hold Space to build weight, release to roll); weight sets how far the wood runs (quadratic in weight, against a pace that is one seeded constant per game) and each wood bends towards its bias side as it slows, so aiming straight at the jack misses; a wood off the back of the green (or into the ditch at the side) is dead; one end of four woods, and one or more resting nearer the jack than the nearest rival wood wins (forged by the crew on the engine and shell above, not by the original creators; rules in bowls-green/bowls.js) | Drag back and release; Left and Right to aim, hold Space |
| Dinghy Race | A bay with a square course of three buoys and back over a start line, the wind from the south-east and shifting a little at each buoy: the boat steers itself buoy to buoy and you trim the sail; it drives best at half the angle between boat and wind, and loses speed quadratically as the trim drifts from that, so every leg and every wind shift asks for a new setting; two seeded rivals sail at a steady pace, first or second place is a win (forged by the crew on the engine and shell above, not by the original creators; rules in dinghy-race/dinghy.js) | Drag left and right along the sheet; Up and Down arrows trim in and out |
| Seed Packets | A potting-shed bench with sixteen face-down seed packets in a 4x4 grid, a watering can and a clock on the wall: tap a packet to turn it face up to show its flower, then a second; a matching pair stays face up and a mismatch turns both back after 0.8 s (taps in that beat are ignored); the layout is a seeded shuffle of eight pairs and the gardener returns after 16 turns (a turn is two packets), so you can afford eight mismatches; the end shows pairs found and turns used, and the best is the fewest turns in a win (forged by the crew on the engine and shell above, not by the original creators) | Tap; arrows + space |
| Ferryman | A river with a near bank, a far bank and a ferry moored at the near landing: tap a passenger to load or unload it, then tap the ferry to cross (or press 1-4 to pick a passenger and Space to cross); the fox, the goose and the grain start on the near bank, and left alone together on a bank the fox eats the goose and the goose eats the grain, so the ferry carries one at a time; three levels, the later ones add a dog, a two-seat ferry and a crossing limit, and a round is won by getting everyone across in par crossings or fewer, the HUD showing level, crossings and par as the rules report them (forged by the crew on the engine and shell above, not by the original creators; rules in ferryman/ferry.js) | Tap; 1-4 and Space |
| Market Barrow | A village lane with four numbered stalls (Mill, Harbour, Orchard, Forge), each with a chalk price board of four numbers, and a barrow that carries up to 10 goods (wool, salt, apples, iron): tap a stall, or press 1-4 or the arrow keys, to wheel there, which costs a day, and tap a good's buy or sell button to trade one; you start day 1 with 100 coins, prices swing between half and one and a half times the usual from a seed, a board shows the price you will meet on arrival at the other stalls, and travelling on day 10 ends the game, a win at 150 coins or more (goods left over count for nothing); the HUD shows the day, coins and barrow load, prices and loads are always written as numbers, and only the stall awnings move on their own (stopped under reduced motion) (forged by the crew on the engine and shell above, not by the original creators; rules in market-barrow/barrow.js) | Tap a stall, then buy or sell; 1-4 or Left and Right to travel |
| Sandcastle Tide | A square beach with a sandcastle on the far edge, rocks scattered over the sand and the sea along the near edge: tap a sand square to pat down a wall (or move a cursor with the arrow keys and press Space), four walls before each tide, then tap tide (or press Enter) and the water floods every square it can reach from the sea, a little further each time, never through a wall or a rock, and washes away half the walls it touches; keep the castle dry through five tides, the HUD showing the tide, the walls left and the dry squares; input is ignored while the water spreads, the surf line ripples (stopped under reduced motion), and sand, water, walls and rocks differ by texture as well as colour (forged by the crew on the engine and shell above, not by the original creators; rules in sandcastle/tide.js) | Tap a sand square, then tide; arrows and Space to build, Enter for the tide |
| Lock Keeper | A flight of three canal locks climbing left to right, red narrowboats waiting at the bottom to go up and blue ones at the top to come down, in a seeded order (three rounds shipped, each with a par): tap a paddle or gate to work it and a boat to move it into or out of its lock, or Left and Right to pick a lock and 1 to 5 for its top paddle, bottom paddle, top gate, bottom gate and a boat; a gate opens only on level water, a paddle fills or empties the lock but not while the far gate is open, two open paddles flood the lock and lose the round, a refusal says why, and the end shows operations against par (forged by the crew on the engine and shell above, not by the original creators; rules in lock-keeper/lock.js) | Tap; Left and Right, 1 to 5 |
| Allotment | A five by five garden of raised beds with a bench holding the next seed packet: tap a bed to plant the packet there (or arrow keys to move and Space to plant); twelve packets in a season and every crop scores 1 plus its own rule for each touching bed (up, down, left, right): beans +3 per sweetcorn, sweetcorn -1 per sweetcorn, carrots -2 per onion, onion +2 per lettuce, lettuce +2 per carrots; 28 points by the last packet wins, against the best layout the rules could find (a seeded hand of eight seasons; forged by the crew on the engine and shell above, not by the original creators; rules in allotment/plot.js) | Tap a bed; arrows + Space |
| Shunting Yard | A railway yard seen from above: a mixed-up train of numbered wagons waits on the arrival track, two sidings hold 3 wagons each, and the departure track takes the wagons only in order 1, 2, 3...; lift the end wagon of a track and set it down on another (nothing goes back to the arrival track), five levels of 3 to 7 wagons, each with a move target (5, 7, 9, 11, 13) that every yard solves within, and the game is won when all five are cleared within the targets' total; every wagon wears its number on a plate, so order never rests on colour (forged by the crew on the engine and shell above, not by the original creators; rules in shunting-yard/yard.js) | Tap a track to lift its end wagon, tap another to set it down; 1-4 pick the tracks, arrows move a ring, Space lifts or sets down |
| Ferris Wheel | A fairground Ferris wheel of 8 gondolas, 3 seats each, with a platform at the bottom and a queue of 20 riders of different weights and ride lengths: tap Board (or the canvas, or press B, Space or Enter) to seat the front rider in the gondola at the platform, or Skip (or S) to send them to the back of the queue, once each; the wheel steps one gondola on a steady beat, 24 steps in all, and riders step off when their ride is done; a balance needle under the hub shows the imbalance (weight times its lever arm about the hub, summed over every rider) with the jam limit marked as a line, and the wheel jams if it passes it; carry 12 riders without a jam to win, the HUD showing riders carried, steps left and the imbalance; rider weights read by size as well as colour (green, amber, red), the front rider's weight is on a plate, and the fairground lights blink (stopped under reduced motion) (forged by the crew on the engine and shell above, not by the original creators; rules in ferris-wheel/wheel.js) | Tap Board or Skip; B boards, S skips |
| Bunting | Five village greens, each with more strings of bunting and more hooks (2 strings of 4 up to 6 of 8), some hooks already hung with a fixed pegged flag: hang the rest so that no two neighbours share a pattern, whether next to each other on a string or joined by a cord where two strings cross; three patterns (pennant, square, circle) and a fourth (diamond) from level 4, each differing by shape as well as colour; tap a hook to cycle its flag, or move with the arrow keys and press 1 to 4 (0 takes it down); each flag hung is a move, taking one down is free, a clash is marked by a red line as well as colour, and all five greens within 160 moves wins (forged by the crew on the engine and shell above, not by the original creators; rules in bunting/line.js) | Tap; arrows, 1 to 4 |
| Mill Race | A stone slab of channel tiles on a green, a sluice gate on its west side and a mill wheel on its east: tap a tile (or move a cursor with the arrows and turn with Space) to turn it a quarter, so the stream runs from the sluice to the wheel; wet channels are wide and blue, dry ones narrow grooves, and the grid grows from 4 by 4 to 8 by 8 over 5 levels, each with a par of turns, so win by clearing all 5 within 6 moves over par in all (forged by the crew on the engine and shell above, not by the original creators; rules in mill-race/race.js) | Tap a tile; arrows and space |
| Stained Glass | A church window of leaded glass panes in a stone frame, with the palette in a tray below it: tap a colour, then a pane, to paint it, and tap a painted pane again to clear it; panes that share a lead line must not match, a clash is marked by a thick dashed lead line, every colour carries its own pattern (stripes, dots, cross-hatch, checks) and a rivet marks a fixed pane; three windows (3, 3 and 4 colours), solved when every pane is coloured with no clash, scoring 100 and 50 more at or under par strokes (forged by the crew on the engine and shell above, not by the original creators; rules in stained-glass/window.js) | Tap, or arrows, 1-4, Space and R |
| Jam Jars | A pantry shelf of 3 to 5 glass jars with their fruit layers (plum, fig, quince) visible through the glass: tap a jar, or press its number key, to pick it up (it lifts and is outlined), then tap another to pour the top run of one fruit into it, as many layers as fit, onto the same fruit or into free space; a rejected pour says why and changes nothing; undo (button or U) reverses the last pour and costs a pour, R starts the shelf again; every jar must end empty or full of one fruit, the HUD showing the shelf, pours against par and points; each fruit carries its own pip pattern (plum dots, fig stripes, quince checks) as well as its colour, so it can be solved without telling colours apart; four shelves, scoring 100 and 50 more at or under par pours (forged by the crew on the engine and shell above, not by the original creators; rules in jam-jars/shelf.js) | Tap a jar, or 1-5, arrows, Space, U and R |
| Library Shelf | An oak library shelf with two runners and books of different widths, the returned book (red, with a ribbon bookmark) waiting at one end and a dashed gap marking where it must reach: drag a book along its runner, or press Tab to choose one and use the arrow keys, to slide it; each book slides only the way the arrow on its spine points and cannot pass another, however far a slide goes it counts as one, undo (button or U) takes the last slide back at the cost of a slide, R begins the level again, and all 3 levels cleared wins, with a bonus for each within par slides (forged by the crew on the engine and shell above, not by the original creators; rules in library-shelf/shelf.js) | Drag; Tab and arrows |
| Skipping Rope | A school yard of tarmac with a grass verge, a hopscotch grid, a fence and a tree, where two friends turn a long rope and you skip in the line between them (you wear a yellow cap; the others wear none): tap the canvas or press Space to jump, and be in the air when the rope touches the ground, which a chalk line on the ground flashes; the rope quickens and another skipper joins every 3 clears, each joined skipper adds 5 to every clear, so win by making 12 clears and lose by landing on the rope (forged by the crew on the engine and shell above, not by the original creators; rules in skipping-rope/rope.js) | Tap the canvas; space |
| Tea Party | A garden table with a gingham cloth, a teapot (round), a coffee pot (tall), a milk jug, a sugar bowl and a cake stand, and two to five guests who differ by hat (top hat, boater, sun hat, bobble hat, party hat) as well as colour: each guest's order (tea or coffee, milk or none, 0 to 3 sugars, a cake) shows in a speech bubble in turn, then you serve from memory by tapping a guest, then the pot, milk, sugar (each tap adds one) and cake (each tap changes it), and the guest again to serve; each right part scores 10 and a perfect cup 20 more, the table grows from 2 to 5 guests over 4 rounds, and 3 wrong parts in a round lose (forged by the crew on the engine and shell above, not by the original creators; rules in tea-party/table.js) | Tap a guest, then the pots; Tab, arrows and Enter |
| Chestnut Roast | A winter street with a shop front, a vendor's brazier and roasting pan, and your paper cone on the pavement: chestnuts pop out of the pan in arcs; drag along the pavement, or hold the left and right arrow keys, to slide the cone under the roasted ones (brown, with a pale split) and catch 15 to win, scoring 10 each; a burnt chestnut has a black, cracked shell with a glowing crack and trails smoke, and catching one costs a life of your 3, shown as paper cones, so let it fall; the pops quicken every 3 catches, a roasted one that hits the pavement only breaks your streak (forged by the crew on the engine and shell above, not by the original creators; rules in chestnut-roast/pan.js) | Drag along the pavement; left and right arrows |
| Chimney Sweep | A cut-away brick chimney stack with four flues side by side, rooftops along the top and a sweep with a top hat and a brush: pull to climb 1 m (the brush then cools for half a second before the next pull and you slide back down), change flue to dodge the round grey smoke puffs rising up the flues, a hit knocks you down 3 m and costs a brush of three, and reaching the roof 20 m up within 60 seconds wins, scored for speed and brushes left; the lives show as brushes (forged by the crew on the engine and shell above, not by the original creators; rules in chimney-sweep/roofs.js) | Tap or space to pull; left/right arrows or swipe to change flue |
| Fence Posts | A meadow staked with a four-by-four grid of posts and one to five sheep grazing in each pen: you and a farmer take turns building one length of fence between two posts, whoever closes a pen's fourth side claims it, its sheep and its flag and fences again, and when all 40 lengths stand the most sheep wins (a tie goes to more pens); your fences are blue rails with striped flags and the farmer's are woven wattle with checked flags, so they differ by pattern as well as colour, and the farmer's turn plays out a length at a time (forged by the crew on the engine and shell above, not by the original creators; rules in fence-posts/field.js) | Tap between two posts; arrows move the highlight, Enter builds |
| Conkers | A school playground under a horse-chestnut tree, your conker (double knot and a chalk cross) and the rival's hanging side by side on their strings, and a power meter that sweeps up and down: tap the canvas, or press Space or Enter, to swing when the meter is where you want it; the gold sweet spot (marked by a notch) does 4 damage, anywhere else up to the hatched end does 1, and the hatched end tangles the strings, does nothing and passes the turn; you and the rival swing in turn (their reading comes from the seed, shown as a red pointer), crack all 3 rivals' conkers (6, 8 and 10 hit points) before yours (12) cracks, scoring 100 for each and 10 for each hit point left; cracks show on the conkers (forged by the crew on the engine and shell above, not by the original creators; rules in conkers/match.js) | Tap, or Space |
| Empire Age | A real-time strategy game on a 44 by 44 map against a red bot (easy, normal or hard): four villagers and a town center each, gather food, wood, gold and stone, build houses, camps, farms and military buildings, climb the Dark, Feudal, Castle and Imperial Ages, research upgrades, train militia, spearmen, archers, knights and rams, and destroy every town center and castle of the enemy; fog of war, a minimap, attack alerts, pause, 1x/2x/4x speed, an autopilot, a save that survives a reload and a best time per level (forged by the crew on the engine and shell above, not by the original creators) | Tap or drag a box to select, tap to command, panel buttons to build and train; arrows move the view, Enter taps its centre cross, 1-9 press buttons |
| Hot Air Balloon | Five flights over patchwork farm fields with hedgerows, a windsock and one field marked with a white cross: hold the burner and the balloon climbs slowly after a short lag, let go and it sinks; the air has three height bands that each blow their own way and a little differently each flight, shown by drifting cloud wisps, three arrows at the left and a pennant on the basket, so you steer only by choosing a height (the ready screen before each flight already shows that flight's winds, so read them first); touching the ground ends the flight and the landing scores by distance from the cross (on it 5, inside its field 3, a neighbouring field 1, trees or the pond 0), each flight from a new launch point; 15 or more of 25 wins; the balloon's shadow on the fields shows where it is over (forged by the crew on the engine and shell above, not by the original creators) | Press and hold the canvas; hold space or the up arrow |
| Go-Kart Fete | Three races of three laps round a village track against three rivals: hold to drive, let go to brake, and a yellow board before each bend shows the fastest speed to enter it at; go in faster and you sit a second in the hay bales; first scores 3, second 2, third 1, and 6 points wins (forged by the crew on the engine and shell above, not by the original creators) | Hold; space |
| Cloakroom | A coat check with two rails of coats, each with a paper tag: turn two tags at a time, a matching pair is handed back and a mismatch turns back after 0.8 s; three rounds of 6, 8 and 10 pairs, 90 seconds each, and more than 12 mismatches in all (or a clock at zero) ends it; the coats sway and light moves under the door, the number and colour on a turned tag mean it plays muted too (forged by the crew on the engine and shell above, not by the original creators) | Tap a coat; arrows move a ring, space or enter turns |
| Tower Duel | A 90-second two-lane card battle against a seeded rival on a field split by a river with a bridge in each lane: elixir fills over time (twice as fast in the last 30 seconds), pick a card from a hand of four (Knight, Archers, Giant, Goblins, Brute, Fireball) and put its troops on your half, and they walk over a bridge to fight and hit the rival towers; Fireball lands anywhere; toppling the rival king tower wins at once, otherwise more towers down, then more tower health, wins on the clock; a homage to Supercell's Clash Royale in idea only (forged by the crew on the engine and shell above, not by the original creators; rules in tower-duel/duel.js) | Tap a card, then tap your half; 1-4 to pick, arrows move a ring, Space plays |
| Lily Pond | Six levels on a six-by-six pond: the frog starts on a lily pad and hops north, east, south or west to the nearest pad in that line, up to three squares away, and the pad it leaves sinks; land on every pad once (level n has n + 3 pads) and the level is cleared, but a frog with no pad left in reach loses the level, which restarts as the same pond and costs a retry; clearing all six levels with three retries or fewer wins, and a seventh retry ends the game (forged by the crew on the engine and shell above, not by the original creators; rules in lily-pond/pond.js) | Swipe or tap beside the frog; arrow keys |
| Curling Rink | One end on a sheet of ice: eight stones slid from the hack toward the house, against four opponent stones already in play (two guards, two in the rings); drag back to set power, tilt the drag to set curl (arrow keys nudge the aim and curl, hold Enter to charge a throw), and hold anywhere or Space to sweep while the stone runs, which lowers the friction so it carries further; stones that touch exchange speed, and one past the back line or over a side line is out; when the last stone stops, your stones in the house nearer the button than every opponent stone score, and 2 or more win; the crowd breathes and the ice glints (forged by the crew on the engine and shell above, not by the original creators; rules in curling/rink.js) | Drag back, hold to sweep; arrow keys, Enter, Space |
| Hedge Maze | A garden maze of hedges carved from a seed, 5 by 5 cells for the first and one cell larger for each one solved, up to 12 by 12: walk from the gate to the stone fountain at the far end of the only path, one cell a step, before the gardener's bell rings 45 seconds after each maze begins (the bell starts afresh with every maze); solving 5 or more by the last bell wins, rules in hedge-maze/maze.js; the fountain splashes (forged by the crew on the engine and shell above, not by the original creators) | Swipe or tap beside the walker; arrow keys |
| Bird Watch | A garden with an oak, a fence, a shed roof and a bird feeder: each round a flock of birds, each a different species with its own silhouette, lands on distinct perches and flies off, then the game names the birds one by one and you point at the perch where each sat; round 1 shows 2 birds, round 5 shows 6, a wrong perch costs one of 3 pairs of binoculars, and 4 rounds cleared wins (forged by the crew on the engine and shell above, not by the original creators; rules in bird-watch/watch.js) | Tap a perch; arrows and space |
| Rain Barrel | A cottage wall with a row of five gutter spouts, a rain barrel on the paving beneath and a yard behind it: rain drips from one spout at a time, and you slide the barrel under the spout before the drop lands (tap a spout, or Left and Right) to catch it for 10 points; any drop that lands elsewhere, or into a full barrel, or while the barrel is being emptied adds to the yard puddle, and the puddle reaching the red line (8) floods the yard and loses; the barrel holds 4 drops and wears a lid when full, and tapping it (or Space) empties it, which costs two ticks of rain but scores 25 more when it was full; 40 ticks of rain, and the clouds drift over the roof (forged by the crew on the engine and shell above, not by the original creators; rules in rain-barrel/gutter.js) | Tap a spout or the barrel; arrow keys, Space |
| Patchwork Quilt | A 3x3 quilt of square patches laid on a cottage bed, each edge stitched in one of four patterns and scrambled by turns and swaps: tap a patch to pick it (it is outlined) and tap it again to turn it a quarter, or with one picked tap another to swap the two; a cross-stitch on a seam marks edges that match and an open gap marks edges that do not, and the quilt is finished when all 12 edges match within 24 moves; a move that raises the matching edges scores 10 and each move left on finishing scores 5 (forged by the crew on the engine and shell above, not by the original creators; rules in quilt/patch.js) | Tap, or arrow keys, Space and Escape |
| Rock Pool | A tide pool of 4x4 rocks with the tide out: for 30 seconds crabs (20 dashes, 1.5 to 2.5 seconds each, planned from a seed) dart in a straight line from one rock to another and are in the open only while they run, each marked by a raised claw as well as its colour; tap the rock a crab is over to scoop it into your bucket for 10 points, or 15 in the first third of its dash, the bucket holds 6 and a scoop with a full bucket is refused, and a miss costs a second before you may scoop again; when the tide returns 80 points or more win; the water at the pool's edge shimmers (forged by the crew on the engine and shell above, not by the original creators; rules in rock-pool/pool.js) | Tap, or arrow keys and Space |
| Welly Toss | A grass field with marker flags every ten metres, white side lines and a windsock showing this throw's crosswind: a rubber boot swings back and forth on your arm, and you let go at the right moment of the arc to land it past the flags, because letting go on the backswing, or drifting over a side line in the wind, is a foul; five throws, the best three distances count (forged by the crew on the engine and shell above, not by the original creators; rules in welly-toss/throw.js) | Tap, click or space |
| Ticket Booth | A seaside pier ticket booth with a till of eight trays (5p, 10p, 20p, 50p, £1, £2 and the £5 and £10 notes, told apart by size and shape as well as colour), a change tray in front of it and a queue of twelve visitors, the front one holding their tickets and payment: tap a till coin to put it in the tray, tap a tray coin to take it back, tap Hand when the tray holds exactly the change due (10 points, 5 more for the fewest coins the till could have used) or Short to send away a visitor whose change cannot be made from the till and tray, which loses them; their payment enters the till when they are served, so it can pay for later change, never their own; three lost ends the day (forged by the crew on the engine and shell above, not by the original creators; rules in ticket-booth/till.js) | Tap a coin, Hand, Short; keys 1-8, Backspace, Enter, S, R |
| Village Darts | A village hall wall with bunting, a dado and a dartboard of twenty numbered segments in the regulation order with treble, double and bull rings at their true radii: you throw from a wobbling aim marker and count down from 301 against a seeded rival, three darts a visit, finishing exactly on zero with a double (a bust sends the score back); each dart reports its segment, ring and points, landed darts stay on the board until the visit ends, your blue darts lob in an arc and have triangle flights while the rival's red darts fly flat and straight with square flights, and the rival's visit plays out a dart at a time (forged by the crew on the engine and shell above, not by the original creators; rules in village-darts/board.js) | Tap, or Space or Enter, to throw; R restarts |
| Carrot Pull | A four-by-six vegetable bed where each carrot grows on a seeded schedule from a pale seed to a sprout (two round leaves, no root showing), then ripe (upright fronds over an orange shoulder) and finally woody (brown leaves splayed flat round a tall pale root), with you the gardener in a straw hat and a seeded rabbit hopping along the rows: walk one square and pull the carrot you stand on, 5 points ripe, 1 woody, 0 for a seed or sprout, before the rabbit reaches it, since it nibbles any ripe or woody carrot it lands on; the day ticks on every 0.6 s for 40 ticks and the HUD shows score, ticks left, carrots pulled ripe and carrots lost (forged by the crew on the engine and shell above, not by the original creators; rules in carrot-pull/bed.js) | Arrow keys or WASD, Space, R; on touch tap a neighbouring square to walk, your own to pull |
| Bumper Cars | A walled fairground floor of nine by nine squares with your car and three seeded rivals, each pointing along its heading: the floor ticks on by itself every half second and your car drives one square a tick, so you steer with the left and right arrows or A and D (or the left and right half of the picture on touch); drive into a rival from behind or the side to shove it a square, shove it into the wall for 10 points, and never meet one head-on, which costs a life of three; keep your lives for 60 ticks to win, R restarts; your car is a blue flat-roofed box with a bar across its front and a flagged pole at the back, and the rivals are round bodies tapering to a pale nose, so they differ by shape and each heading reads without colour (forged by the crew on the engine and shell above, not by the original creators; rules in bumper-cars/floor.js) | Left and right arrows, A and D, or tap the left or right half |
| Apple Bobbing | A fete water tub seen at the fixed 30-degree camera with five apples bobbing in it, each on its own seeded period and phase, the tub ticking on every half second for 60 ticks: tap an apple or pick it with the arrow keys to aim (a ring on the water round it), and tap it again or press Space to dunk; an apple at the top of its bob wears a sparkle above it, so it reads without colour, and a dunk there scores 30 (10 one tick either side) and takes the apple out to the lawn, while any other dunk splashes and costs a breath of three; catch all five to win, R starts a fresh seed (forged by the crew on the engine and shell above, not by the original creators; rules in apple-bobbing/tub.js) | Tap an apple, tap it again; arrow keys, Space, Enter, 1-5, R |
| Cake Stall | A fete cake stall under a striped awning, seen at the fixed 30-degree camera on a gingham cloth with two plates: the customer's order, a seeded stack of cake layers (sponge, jam, cream, icing), is shown on the left plate for a few ticks and then covered by a silver cloche, and you stack the layers back on the right plate from memory with the four buttons along the bottom (or keys 1 to 4, or the arrows to mark one and Space or Enter to place it); each kind differs by pattern as well as colour (sponge crumb dots, jam diagonal stripe, cream wavy swirl, icing checks) so the order can be remembered without colour, pips under the plates count the layers, a wrong layer costs a life of three and shows the same order again, and each finished order scores 10 a layer and the next is one layer longer, eight rounds in all; R starts a fresh seed (forged by the crew on the engine and shell above, not by the original creators; rules in cake-stall/order.js) | Tap a layer button; 1-4, or arrows with Space or Enter; R |
| Tide Crossing | A causeway of stepping stones in a rising sea, drawn under the fixed 30-degree camera with hard shadows screen-right: step from the shore to the island flag, one stone or one wait per tick, before the tide table covers your stone; each stone stands at its own height and a tide gauge shows the water, a stone that goes under next tick has a rippling edge as well as its colour, and the HUD counts ticks against par (forged by the crew on the engine and shell above, not by the original creators; rules in tide-crossing/causeway.js) | Arrow keys or WASD, Space to wait, or tap a stone |
| Pumpkin Patch | A walled pumpkin patch seen at the fixed 30-degree camera: a grid of soil plots with a pumpkin in each and grey flagstones that take no water, and a watering can hovering over the plot under your cursor; each plot shows the waterings it still needs, and a pour on a plot waters it and its four neighbours by one; bring every pumpkin to exactly its need (it swells under an open leaf, where a dry one is a small bud) and one drop too many collapses it into a rotten heap; six levels, each with a par, and U undoes, R restarts (forged by the crew on the engine and shell above, not by the original creators; rules in pumpkin-patch/plots.js) | Tap a plot; arrow keys, Enter, U and R |
Run
Requires Node.js; nothing to install.
node serve.mjs
Then open http://127.0.0.1:5300/. PORT and HOST override the port and address; bun serve.mjs works the same. Any static file server works, and the site deploys as plain static files.
On the forge, forge.json runs bun serve.mjs with health check /. State is only the play counts, under FORGE_APP_DATA. bun test checks that the server answers every game page and track.
Plays
/stats/ ranks the games by how much they are played, and /api/stats returns the same rows as JSON. Each game page reports three anonymous events to POST /api/play (an Origin check against browser cross-site posts, which a script can still forge, so the endpoint is also limited as below): the page opened, the first tap or key press (a play), and the seconds the page stayed visible after that. Only totals per game are kept, with no ids or cookies, in plays.json under FORGE_APP_DATA (in memory when it is unset). stats.mjs holds the counting; it needs no dependency, so the file is plain JSON rather than SQLite, which would tie the server to Bun. Limits: 60 events a minute per client address (the socket address, never a header) and 600 a minute per game, answered with 429; a play is refused unless an open is ahead of it and time unless plays can account for it (at most 3600 s each), so forged events cannot show a ratio real ones never could. They cannot stop someone slowly inflating a game within those bounds, so treat the numbers as a guide. Counts are flushed to disk on SIGTERM. Not recorded yet: a finished round, since each game ends in its own way.
Share cards
Every page carries Open Graph and Twitter tags (between og:begin and og:end), and og/<id>.png is a 1200x630 card: the game's own opening frame, scaled 2x on white; og/home.png is a grid of all of them. Because the games are static, bun scripts/og.mjs makes them without a browser: it runs each page's scripts in a vm against a stub DOM, captures the canvas pixels and writes the PNG (zlib only, no dependency) and the tags. Run it after adding a game or changing a scene; serve.test.mjs fails when a card or its tags are missing or stale. Scenes are rendered with a seeded Math.random, so the cards are byte-for-byte reproducible. Absolute URLs use ORIGIN at the top of the script (the forge production site), so change it there if the site moves.
Add a game
Create <id>/index.html, load ../shell.js, call PixelPlays.mount({ id, rules }), set window.PP_SCENE, load ../engine.js, then add the game to GAMES in shell.js with a one-line blurb; the home page lists every GAMES entry as a card with its kind, input and accent, searchable and filterable by kind (a static fallback list for no-JS is kept in sync by serve.test.mjs, so add the matching <li> to index.html too, with the same data-kind, data-input, --accent and tags line). Then run bun scripts/og.mjs to make its share card and tags. The page needs a #mute button. Put its background track at <id>/<id>.mp3, or set music: false on its GAMES entry when it has none (Stone Skip, Kite Hill, Paper Route, Harbour Crane, Pizza Toss, Sled Run, Pottery Wheel, Row Home, Orchard, Lighthouse, Market Scale, Window Washer, Domino Run, Campfire, Beekeeper, Bread Oven, Ice Fishing, Tightrope, Tram Line, Archery, Bell Tower, Espresso Bar, Goalkeeper, Wood Split, Signal Box, Soapbox Derby, Sheepdog, Firefly Jar, Hot Air Balloon, Cloakroom, Go-Kart Fete, Empire Age, Egg Basket, Dinghy Race, Coconut Shy, Bowls Green, Seed Packets, Hoopla Stall, Lock Keeper, Ferryman, Tower Duel, Sandcastle Tide, Hedge Maze, Allotment, Lily Pond, Market Barrow, Curling Rink, Shunting Yard, Ferris Wheel, Bird Watch, Bunting, Patchwork Quilt, Mill Race, Rain Barrel, Rock Pool, Stained Glass, Conkers, Jam Jars, Library Shelf, Skipping Rope, Tea Party, Chestnut Roast, Chimney Sweep, Welly Toss, Fence Posts, Ticket Booth, Carrot Pull, Village Darts, Pumpkin Patch, Tide Crossing, Bumper Cars, Apple Bobbing and Cake Stall play on their own sound effects). Rules that need testing go in a plain script with a .test.mjs beside it, as in kite-hill/model.js.
town.jsis the town's layout, pure data with no drawing:window.PixelTown.layout(GAMES)gives one isometric building per game (plot, screen position under the fixed 30-degree camera, accent, id) with trees in the spare plots, andpick(town, x, y, filter)gives the id of the front-most building under a screen point, or null. Nothing loads it yet; the drawing and the page are the second half of #99.shell.jsprovides the game menu, the Rules popup, the credits and the background music. The menu lists the games grouped under their kind (the not-yet-made ones last, under Soon) and scrolls within the screen; type in its find box to narrow it by a word of the title, kind, input or blurb (keys typed there never reach the game), and press Escape to close it.engine.jsprovides the camera, depth buffer, colour ramps, shadows, edges, figures, trees and wind as globals.- A game supplies its own
groundColor,bake,updateanddraw.
Visual rules
- One small place floating on pure white, with no UI chrome around it.
- Low-resolution canvas (360x280), nearest-neighbour upscale, integer device-pixel scale.
- Fixed 30-degree orthographic camera; a ragged dithered edge dissolving into white.
- Real-time rasteriser with a depth buffer, four shade levels and Bayer dither.
- Long, hard cast shadows falling screen-right. One readable physical action per game.
- Hue-shifted colour ramps: shadows drift to violet, lights to warm yellow.
- Depth-buffer edges: darker where an object sits in front, a warm rim on upper-left edges.
- Large flat surfaces use closely spaced tones; wide steps make the dither read as a checkerboard.
- Characters stay sharp: flat bands with no dither, a dark silhouette on their own pixels, patterns at least 3px wide.
- Ambient motion only where the scene names it: grass and leaves sway in whole pixels at 8 fps, Village Darts' hall bunting flutters (its wobbling aim marker is play, not ambience, and still moves), Tram Line's waiting passengers shift their weight, Espresso Bar's machine steam and awning fringe move, Sheepdog's crow on the wall shifts and dips its head, Hot Air Balloon's windsock and cloud wisps drift, Cloakroom's coats sway and the light under its door moves, Curling Rink's crowd breathes and its ice glints, Go-Kart Fete's chequered flags flutter and its spectators wave, Egg Basket's hens peck and the straw in its basket stirs (its rolling eggs are play, not ambience, and still move), Ferris Wheel's fairground lights blink, and Dinghy Race's water ripples come and go and its start-line flags flutter (the boats are play, and still sail), and Coconut Shy's bunting flutters and its stallholder shifts and waves (the thrown ball and wobbling coconuts are play, and still move), and Bird Watch's oak leaves stir (the birds landing and leaving are play, and still move), and Bowls Green's hedge leaves stir and a cloud shadow drifts across its green (the rolling woods are play, and still move), and Seed Packets' wall clock ticks its second hand and its watering can drips, and Hoopla Stall's bunting flutters (the tossed ring is play, and still moves), and Lock Keeper's water surface ripples (the boats, gates and rising water are play, and still move), and Ferryman's river ripples (the ferry and its passengers are play, and still move), and Tower Duel's river ripples shift and the king towers' pennants stir (the troops and the fireball are play, and still move), and Hedge Maze's fountain splashes (the walker is play, and still moves), and Allotment's planted crops sway (the planting is play), and Lily Pond's water glints shift (the frog, the sinking pads and their ripples are play, and still move), Market Barrow's stall awnings flutter (the barrow is play, and still moves) and Shunting Yard's engine puffs steam (the wagons are play, and still move) and Bunting's flags stir in the wind, and Patchwork Quilt's window curtain stirs (the patches are play, and still turn and swap), and Mill Race's water glints shift (the wheel turns only when the rules say the stream reaches it: that is play, and under reduced motion it holds still and shows wet blue paddles instead), and Bumper Cars' fairground bulbs blink (the cars are play, and still move), and Apple Bobbing's water ripples come and go (the bobbing apples are play, and still move), and Cake Stall's awning fringe flutters (the cakes and the cloche are play), and Rain Barrel's clouds drift over the roof (the falling rain and the sliding barrel are play, and still move); and Rock Pool's edge water shimmers (the crabs are play, and still dash); and Stained Glass's sunlight creeps across the panes (the painting is play); and Jam Jars' sunlight creeps across the empty glass; and Library Shelf's lamplit dust motes drift (the sliding is play, and still moves); and Conkers' strings and conkers sway a little (the meter and the swings are play, and still move); and Chestnut Roast's brazier coals flicker and its smoke drifts up (the chestnuts, and the smoke trailing a burnt one, are play, and still move); and Chimney Sweep's rooftop weathervane turns (the smoke puffs and the sweep are play, and still move); and Welly Toss's marker-flag pennants and windsock flutter (the swinging boot and its flight are play, and still move); and Fence Posts' sheep dip their heads to graze and its flags flutter (the fences and claims are play); and Ticket Booth's front visitors shift their weight (the coins, trays and the queue sliding up are play, and still move); and Carrot Pull's ripe fronds sway (the growing, the rabbit's hops and the gardener are play, and still move); and Pumpkin Patch's open leaves stir (the tilting can is play, and still moves); and Tide Crossing's lapping water foam slides (the tide level and the walker are play, and still move); all of it stops under
prefers-reduced-motion.
Empire Age
empire/ is the one game that does not use engine.js: a strategy game needs a flat map, many units and a minimap, so it has its own small renderer (paint.js, an isometric 360 by 280 pixel buffer, tiles 16 by 8) and the rules live apart from the page, each module named for one concept and none depending on the DOM:
| File | What it holds |
|---|---|
tables.js | resources, the four ages, units, buildings and technologies as data |
grid.js | A* path finding on the tile grid |
realm.js | the world: newGame, tick, and the commands order, place, train, research, ageUp, cancel, rally, demolish, resign; snapshot and a defensive restore |
regent.js | the bot, which plays by those same commands (the autopilot runs it on the person's side) |
paint.js | draws the state; pick.js turns a screen point into the entity under it |
smart.js | what a tap means for the selection (move, attack, work, build) |
panel.js | the command panel as plain data, and what each button does |
session.js | the one save slot and the best time per level, in localStorage |
The simulation advances in 0.1 s steps with a seeded random, so a game is the same every time for the same seed and commands, and a bot-versus-bot game runs in a test. Nothing is fetched or generated outside the page: no assets, no dependencies, and the only server call is the anonymous play count every game makes. bun test empire/ covers the rules, the bot, saving and the panel; empire/page.test.mjs plays the real page through the stand-in browser.
License
The code is under the MIT License. The music is not: see MUSIC.md.
Playtests
playtest.mjs boots a game page for real (its inline scripts, shell.js, engine.js) in a vm with a small DOM stand-in, then playtest.test.mjs plays every game folder with a seeded stand-in for a person: taps, holds, drags (one pointer move per frame) and key presses. Each game must boot without errors, paint a real picture, idle without failing, and, played, differ from an untouched twin of the same seed advanced by the same frame count: in its picture and in its HUD text (score, status). The keyboard alone must part the two as well. So a game that ignores its input fails. A new game folder is picked up automatically. It adds no dependency. The full bun test took about 524 seconds locally before the catalogue's cost was trimmed and takes 259 seconds now (1267 tests, playtest.test.mjs 188 of them), inside the deploy's 600-second limit. The trims: the first two tests of a game share one boot (the idle test carries on from the first test's two seconds), the round test's untouched twin is that same boot carried on to the round's moment with its drawing switched off (stopDrawing: only its text is read), and serve.test.mjs captures every card once and a second time only for the first, middle and last game to check determinism (every committed card is still compared). Each game also has a budget test: its playtests together may take 15 seconds plus 300 ms per second of the scripted round (the round is played twice), and the test fails naming the game and its time when a page is slower, so a slow page is caught in its own pull. The scripted rounds remain the bulk of the time; a long round such as go-kart's 178 seconds costs about 14 seconds. The page's scripts run as one strict function with the hot globals (Math, typed arrays, document) bound once, because a vm context looks every global name up through an interceptor, which made each frame two to three times slower. A scripted round, with its idle twin, takes a few seconds of test time, except the fixed-length timed rounds (wood-split's 60 s is about 30 seconds of test time), so a round test is allowed 120 seconds.
rounds.mjs holds one scripted round per game (a table keyed by game id: a beat of taps, drags, holds or keys, repeated until the HUD line that ends the round appears, watched after every frame). playtest.test.mjs plays each script, asserts the end line matches the row's outcome pattern (the win or loss, with its number), and that an untouched twin run for as long does not reach that outcome, and fails when a game folder has no script. A new game must add its row there.
The town
The home page opens on a small isometric town above the list: one building per game in its accent colour, laid out by town.js (pure data, tested) and drawn by plaza.js with the colour ramp and dither of the engine (a small copy in ramp.js, so the home page loads no renderer; ramp.test.mjs fails if the two drift). The look follows the engine and shawn.'s Small plays reference, as the footer credits. Tap or click a building to play its game; hover it, or hover or focus its card, to lift it and see its name. Search and the kind filter grey out the buildings that no longer match, and those cannot be tapped. The list underneath is the same as ever and remains the way in without scripts and from the keyboard; the canvas is aria-hidden, hidden without JavaScript, has its space reserved with aspect-ratio (and on a phone is at least 800px wide and scrolls sideways inside the page, so a building stays about thumb-sized), and redraws only when something changes. plaza.test.mjs runs the real scripts against a stand-in canvas.