deniz.in

Markets

Weather

Loading weather

· via dev.to (home feed)

Samurai Vengeance packs a 1000-stage action game into one 650 KB HTML file

A developer shipped Samurai Vengeance, a 1000-stage samurai action platformer with ten bosses, as a single 650 KB HTML file of plain JavaScript, CSS and canvas — no engine, no build step, with a few caveats.

Samurai Vengeance packs a 1000-stage action game into one 650 KB HTML file

A developer has released Samurai Vengeance, a side-scrolling samurai action platformer with 1000 stages, ten bosses, a three-hit combo, a parry and a dash, packaged almost entirely in a single index.html file. According to the developer's post on dev.to, the file is about 650 KB and roughly 8,800 lines of plain JavaScript, CSS and markup, rendered on HTML canvas with no game engine and no build step. The single-file choice was deliberate: as a first game, it meant nothing to configure — open the file, change a line, reload — and deployment was just uploading a static page to Netlify.

Three canvases, capped pixel ratios

The page stacks three full-screen canvases: one for the parallax background, one for gameplay and one for weather and foreground effects. Splitting them lets each layer carry a different rendering cost. On high-density phone screens the canvases are scaled by the device pixel ratio but capped at 2, because all three layers redraw every frame and pixel fill work scales with the square of the ratio — a 3x ratio would push more than twice the pixels of 2x for a difference the developer considers hard to see on a phone held at arm's length.

The art is not actually in the file

This is where the one-file claim bends. The painted art ships as fourteen separate PNG atlases — one each for the player, the ninja enemies, archers and scenery, plus one per boss. Each atlas is a sheet of body parts rather than animation frames; a table in the code gives every piece its atlas coordinates and a pivot point at its joint, and the painted pieces are drawn onto a procedural skeleton that animated the characters before any art existed. Every character also has a fully procedural drawing path, and if no atlas loads, the game logs one line and keeps that character procedural — a missing file degrades gracefully instead of crashing.

That fallback cost bandwidth at first. Atlases originally requested a sprites/ folder that never existed, so every visit began with a 404, and three sprite sheets that were never made were also requested each time. The fix was to try the site root first and gate the unused sheets behind a disabled flag.

A soundtrack with no audio files

There are no music files either. The soundtrack is generated live with the Web Audio API, written in the five-note miyako-bushi scale (D, E-flat, G, A, B-flat) used in much koto and shamisen music. Each instrument is a small function built from oscillators, filters and noise: a koto pluck, a shamisen, a breathy shakuhachi, a taiko drum made from a pitch-dropping sine plus filtered noise, and a low pad. Timing relies on a lookahead scheduler — a 25 ms interval queues notes about 150 ms ahead on the audio clock — so the music stays in time even when a frame hitches, and the boss theme speeds up and adds drums when a boss enters its second phase. Sound effects are short synthesized tones as well.

Local saves and a privacy-friendly counter

Progress saves without an account: the core save is one JSON object in localStorage (key sv5, with a fallback to the older sv4 so earlier players kept their progress), and every read and write is wrapped in try/catch so storage failures in private browsing never stop the game. Optional sign-in syncs progress through Firebase Firestore and adds the player to a leaderboard, with the SDK loaded from Google's CDN.

Because Firebase Analytics only starts after consent and Netlify request counts cannot distinguish a three-second bounce from a forty-minute session, the developer built a minimal counter. The page sends plain, allowlisted event names — arrived, start, stage clears, death, quit, tutorial steps — to a Netlify function that records daily integer totals in Firestore, with no cookies, identifiers or IP addresses. Events are batched on a 1.2-second timer and delivered via navigator.sendBeacon with a text/plain content type, which sidesteps a CORS preflight. The queue flushes on pagehide, and the arrived event is deferred until the page becomes visible, since apps that prefetch links in the background were inflating that count.

What one file costs

The tradeoffs are candid. Every change re-ships the whole 650 KB page; the server sends no-cache so an unchanged page costs a 304 rather than a full download, which mattered on metered hosting bandwidth. At 8,800 lines, navigation means searching rather than browsing, and some bugs came from two sections drifting apart — a hit box defined in one place while the slash effect drew from different numbers elsewhere. A thrown error in drawing code used to halt the entire game; the loop now wraps draw and update in separate try blocks so one bad frame stays one bad frame. As the developer puts it, "one file" really means one file of game code.

Why it matters

The project is a concrete demonstration of how far vanilla web platform features — canvas, Web Audio, localStorage, sendBeacon — can carry a complete, commercial-feeling game with no engine, framework or toolchain. Just as useful are the portability patterns: procedural fallbacks for art, error isolation per frame, defensive storage access, and an analytics approach that answers "did anyone play this?" without tracking anyone. For small web projects of any kind, those techniques transfer directly.

  • #javascript
  • #html5-canvas
  • #web-audio-api
  • #game-dev
  • #web