← OVERCLOCK / dirge protocol THE GUIDE · how it was built
[ design showcase · build notes ]

a dungeon drawn from nothing.

Overclock looks like a game capture site, but there isn't a single image file in the repository. Every screenshot, icon and face is drawn at runtime by ~200 lines of canvas code — no framework, no animation library, no build step. Here's how.

> the idea

An indie roguelike launch page has one honest advantage over a AAA one: the studio can show its actual craft instead of a cinematic trailer. So the site's pitch is the demonstration — the hero "key art" is a live pixel renderer, the screenshots are the same renderer in four palettes, and the patch notes type themselves out in a terminal. If the page itself feels like the game, the wishlist button doesn't need to shout.

The layout borrows three shipped patterns (researched on Mobbin): the streaming-service title hero (logo lockup + metadata strip + loud/quiet CTA pair), the store-page "latest updates" version notes promoted to a full devlog section, and the marketplace thumbnail-rail screenshot gallery.

> the stack: zero dependencies

> signature #1 — the pixel renderer

One painter function draws every dungeon on the site. It's deterministic: a seeded mulberry32 stream decides bricks, cracks, crystals and furniture, so the same seed always produces the same room — at 8 ticks per second, sixty times a minute, without the scene rearranging itself:

main.js — the seeded stream
const mulberry32 = (a) => () => { /* deterministic 0..1 stream */ };

// layout, bricks, cracks, crystals: from the SEED stream
// flames, fog, portal breathing:   from a tick HASH
const hash = (a, b) => { /* stateless — never disturbs the seed */ };

paintDungeon(ctx, 192, 128, PALETTES.crypt, seed, tick, knight);

The split matters: anything structural reads from the seeded stream, anything alive (torch flames, drifting fog dither, the portal glow) reads from a stateless hash of the tick. That's why the room holds still while its torches flicker.

Sprites are authored as ASCII maps — a knight is twelve characters wide, and each character indexes a palette:

main.js — the knight, frame 0
const K_BODY = [
  '....PP......',     // P plume (magenta — amber in god mode)
  '...PHHHH....',     // H helm
  '..PHHHHHH.S.',     // S sword
  '..HHVVHH..S.',     // V visor
  ...
];
drawMap(ctx, K_BODY, KNIGHT_COLORS, x, y, flip);

The walk cycle is three leg frames swapped at 8 fps, with a one-pixel bob. The same drawMap draws the feature icons, the platform glyphs and the two founder faces. The four "screenshots" are the same painter with different palettes and seeds — flooded floors get a shimmer band, the furnace gets rising embers, the garden grows dithered vines.

> signature #2 — the CRT pipeline

The CRT is layered, cheapest-first, and every layer is plain CSS:

main.js — the glitch scheduler
// fires ONLY on scroll-section change, never on focus,
// never with reduced motion, and never twice within 6s
if (!armed || reduced() || now - lastGlitch < 6000) continue;
document.body.classList.add('is-glitching');
setTimeout(() => document.body.classList.remove('is-glitching'), 140);

> details that matter

> ship it on github pages

Everything is relative-path and build-free, so deploying is three commands:

overclock@dirge:~ $
gh repo create overclock-games --public --source . --push
gh api --method POST /repos/USER/overclock-games/pages \
  -f 'source[branch]=main' -f 'source[path]=/'
# live at https://USER.github.io/overclock-games/ in ~1 min

A .nojekyll file tells Pages to serve the folder as-is. No pipeline, no CI — the same way the site has no dependencies.


Overclock is a design-showcase concept — the studio and the game are fictional, the renderer is real. See the repository README for the full demo-vs-real map.

← back to OVERCLOCK