CHIVA / Adventure Games

Aventure Boy

Aventure Boy — shipped as *Whispers of the Meadow* — is a hand-drawn storybook platform adventure about a boy in an oversized yellow hoodie and his small...

8/14/2026Views: 0Launches: 0

1. The Concept

Aventure Boy — shipped as *Whispers of the Meadow* — is a hand-drawn storybook platform adventure about a boy in an oversized yellow hoodie and his small fluffy companion. The stars have gone from the sky above the meadow, and the two of them cross ten areas at night to put them back: down under the hills, then up past the weather to the place the stars were going. The sky brightens behind you as you return them, so the backdrop is the progress bar.

The design constraint was the whole idea: **a complete game that runs by double-clicking a file.

** No install, no server, no build step, no toolchain. That one rule decided everything downstream — every sprite, backdrop layer and interface element is drawn procedurally to canvas at load time, and every sound is synthesised in the browser, so the game is playable with no asset files present at all. Real art and audio can be dropped in later to override the generated versions, one file at a time, and the game notices without being told.

What is in it: 10 areas across 8 visual themes, 215 star placements, 125 hazards, signposted checkpoints, lever-and-bridge puzzles, magic flowers, a companion with its own follow AI and moods, and a full set of menus. It plays with a keyboard, a gamepad, or a thumb on a phone held sideways.

---

2. The Stack

The Stack: HTML5 + Canvas2D in plain HTML, CSS and JavaScript, and nothing else. There is no package.json, no node_modules, no framework, no bundler and no CDN link anywhere in the project — the repository *is* the build.

| Piece | Choice | Why | | --- | --- | --- | | Rendering | Canvas2D at a fixed 960 × 540 virtual space | Letterboxed and DPR-scaled to any screen; all layout maths is in one coordinate system | | Structure | Plain <script> files, each an IIFE hanging its exports off one global (Meadow) | Keeps file internals private with no module loader, so file:// works | | Load order | The <script> list in index.html *is* the dependency graph | Explicit and readable; scenes resolve each other at call time to break the cycle | | Art | Procedural — brushes, characters, props, tiles, backdrops baked to offscreen canvases at boot | 36 registered sprite generators; nothing to download | | Audio | Web Audio API: synthesised effects and a generative score, with a file-first override | Sounds correct with zero audio files; real ones simply take over | | Persistence | localStorage, defensively | A corrupt or blocked store degrades to defaults and never throws into gameplay | | Input | One Input layer over keyboard, gamepad and multi-touch pointers | Everything raises the same named actions, so the game logic never learns which one you used |

Size: 59 JavaScript files, roughly 17,900 lines — about 4,800 in the art pipeline, 2,800 in engine services, 2,300 in scenes, 2,300 in interface, 2,100 in entities and 2,000 in level data.

The AI Assist: Claude Code (Opus 5) driven from the terminal, used as the implementing engineer rather than an autocomplete — reading the existing architecture, proposing the approach, writing the code, and reporting back what it could and could not verify.

The interesting part was verification. No browser automation was available in this environment, so "does it work?" could not be answered by looking. The answer was to build five Node harnesses that load the *real* source files under a minimal DOM shim and exercise them:

measures every menu's layout

which source each sound actually came from — run twice, once as a served page and once as a page opened off the disk

That harness suite is the reason the bugs in section 4 were caught as arithmetic rather than as screenshots.

---

3. The 1 Day Sprint

**Morning (Hours 1–3): The foundation.

** The shell first — canvas, fixed-timestep game loop, virtual resolution with letterboxing, the scene stack with fade transitions, and the input layer. Then the part that decides whether a platformer is worth playing: movement feel. Acceleration and friction, variable jump height, a lighter apex and heavier descent, 120 ms of coyote time, a 140 ms jump buffer, landing squash and dust. Alongside it, the procedural art pipeline — a painterly brush kit, then the characters, props, tiles and four parallax backdrop layers built on top of it, baked once at boot and blitted thereafter.

**Afternoon (Hours 4–6): The game itself.

** Collision (AABB with one-way platforms, ramps and a broadphase), then the things that live in the world: enemies, collectibles, checkpoints, moving platforms, levers, gates, magic flowers. Ten levels authored through a small builder vocabulary so the level files read as data rather than code. The companion's follow AI — it walks a delayed breadcrumb trail of the player's own footsteps instead of chasing, which is why it stays out of the way. Then the interface: a single "printed page" kit of paper plates, foil-stamped titles, letterpress type and a ribbon bookmark, with every screen built from it. Finally the audio — synthesised effects and a generative score that changes per theme.

**The Finish Line (Hours 7–8): Making it a mobile game.

The last stretch was the one that turned a desktop game into something you can hand to a child on a phone. An on-screen thumb pad drawn in the same paper kit: a stick that stays put and reads direction from wherever the thumb landed, a jump button, and an action button with no name of its own — it wears the verb of whatever is in reach (PULL at a lever, LIGHT** at a flower) and dims when there is nothing to do. Sound and pause moved into the top margin as screen furniture that answers a mouse and a finger alike. Supplied audio was wired in over the synthesised versions. Settings was rebuilt as a two-column spread with thumb-sized rows. The dialogue system was cut entirely, and the light on stars and checkpoints was pushed up so the two things worth crossing a level for are the two things that glow.

---

4. The Roadblocks

Roadblocks: almost all of them were the *platform's* rules rather than the game's logic. In order of how much time they cost:

**1. file:// blocks fetch() — so no audio file had ever loaded.

** The headline feature is "double-click index.html". But browsers refuse fetch() on file:// URLs, and every sound was fetched. Artwork was unaffected, because it loads through <img>, which *is* allowed. The result was a game that silently played its synthesised fallback forever and gave no sign anything was missing. The fix was to give audio the same reach the art already had: anything that cannot be fetched is loaded into an <audio> element and played from there. The level, the mute and the whole nested ducking stack had to be mirrored onto that path, since an element does not sit on the Web Audio bus.

**2. Autoplay policy silently ate the soundtrack.

** The menu asks for music the moment it appears — before the player has clicked anything — so the browser refused it. The rejection was being swallowed by a .catch() whose comment claimed it was "waiting on a gesture" while nothing was waiting. The Web Audio path had hidden this: a buffer source started in a suspended context simply carries on once the context resumes, but an element that was refused stays paused for ever. Now every click and keypress retries it.

**3. Layout placed by arbitrary numbers, not by a frame.

** The first version of the touch pad had an action button orphaned in the middle of the picture, a pause icon floating in the sky, and stick chevrons pointing the wrong way from a sign error. The real fix was structural: layout code now returns its geometry *without drawing it*, so separations, margins and collisions are checked by arithmetic.

**4. A contents card sized for five chapters, drawing ten.

** The main menu's table of contents was 192 px tall with rows at a 30 px pitch — fine for the five levels it was written for, and quietly spilling chapters six to ten down over the meadow once there were ten. Its roman numerals ran out at V too, so half the list was numbered in arabic.

**5. Object identity, not logic, froze the mute icon.

** Saving a setting built a brand-new settings object. The HUD holds the object it was handed at construction, so it went on reading the old one and drawing the state from before the click — while the sound itself changed correctly. Settings are now edited in place, one object for the session.

**6. requestFullscreen needs a user gesture, and rotating a phone is not one.

** "Go fullscreen when the phone turns sideways" cannot be done directly. Rotating now *arms* the intent and the next touch spends it — which is the same gesture anyway, because the first thing anyone does after turning a phone is put a thumb back on the controls.

---

5. Key Takeaways

**Layout that can be measured is layout that can be checked.

** Three of the six roadblocks were geometry that looked plausible in code and wrong on screen. Every one of them became impossible the moment the layout was expressed as a function returning numbers instead of a sequence of draw calls.

**The environment you ship to is the one to test in.

** The audio bugs were both invisible under test and obvious in use, because the tests ran where fetch worked and autoplay did not exist. Running the same harness twice — once as a served page, once as a page opened off the disk — turned both into failing assertions.

**Silent degradation is a debugging trap.

** Every audio path was written to fail softly, which is right for a player and terrible for whoever swapped a file in and cannot tell whether they are hearing it. The system now prints exactly where each sound came from — file, file (media) or synthesised.

**A control should name its action, not itself.

A button labelled USE teaches nothing and does nothing most of the time. One that reads PULL** at a lever and dims when there is nothing in reach has turned its own state into the answer to "is there anything here?"

**Removing is a feature.

** Cutting the dialogue system took out a file, 27 conversations, ten intros and a builder — and the game got lighter and faster to play for it.

My final thought: the zero-dependency constraint looked like the hard part and turned out to be the easy part — 17,900 lines of plain JavaScript will happily draw a whole illustrated game with nothing installed. What actually cost the time was the browser's own rules: what a page may fetch, when it may make a sound, and what counts as a gesture. Those are not things you design around at the start; they are things you discover the first time you hand the game to someone on a different device. The lasting value of this build is not the ten levels — it is the harness suite that can now answer "does it still work, and where does that sound come from?" without opening a browser at all.