← WORK

A playable Super Mario in ~10KB of gzipped TypeScript

4 min read

The finale of this site is a green pipe. Click the portrait head hovering over it and you drop into a playable Super Mario-style game where the head IS the player. The whole game — physics, renderer, two levels, koopa shells, breakable bricks, a power-up system, and the background music — ships as roughly 10KB of gzipped JavaScript.

Fork the source, not the bundle

The game started as a vendored build of an open-source clone (iam-veeramalla/super-mario-mimic, Apache-2.0): a 4MB blob of prebuilt assets. The first decision was to fork the SOURCE instead and build it as a second entry of the site's own Vite pipeline. Same game, one toolchain, and the bundle collapsed from 4MB to ~27KB raw because nothing ships that isn't imported.

Draw everything, load nothing

There are no sprite sheets. The player is a single baked portrait PNG; everything else — sky gradients, parallax hills, brick platforms, goombas, coins, the waving flag — is drawn with Canvas 2D calls each frame. A goomba is two ellipse feet, a quadratic-curve cap, and two angry eyebrow strokes. This isn't just a size trick: procedural drawing made localization trivial (the Persian mode redraws the flag with «سپهر» on it) and made mustache mode a ten-line easter egg.

The music is a scheduler, not a file

The obvious move was to ship the Nintendo theme. The obvious move is also copyright infringement, so the game plays an ORIGINAL 8-bar chiptune loop composed as note tables — square-wave lead, triangle bass, noise hi-hats — scheduled with the standard WebAudio lookahead pattern. The entire soundtrack is a few hundred bytes of note names.

Physics is where the real bytes went

The feel comes from the same tricks the 1985 original used: acceleration and skid on the ground, lighter gravity while rising with the jump held, a hard cut when released, coyote time (~90ms of jump-grace after walking off a ledge), and jump buffering. Collision is axis-separated — move X, resolve X, move Y, resolve Y — which is the difference between "sometimes clips through platforms" and "never does". None of this needs a library. It needs about 200 lines of care.

The pause screen shows the byte budget as a bar chart against an average React todo app. It is a flex. It is also the point: constraints are a design material.

More notes