Skip to content
>GLB_
Go back

Building an AI oracle slot machine with nothing but canvas

// tools · frontend

TL;DR

A single self-contained HTML file that turns “what’s your AI doom take” into an actual game: pull a lever, stop four spinning reels on the right moment, then play a hand of blackjack against the house to decide how “legendary” your prophecy is. The result renders to a <canvas> certificate you can download as a PNG. No framework, no backend, no build step.

The problem

The obvious version of this idea is a form: pick a stance from a dropdown, type your prediction into a textarea, hit submit. It works, but it’s boring the second you look at it — it’s a survey wearing a fun hat. Nobody wants to fill out a form to get a joke.

The actual goal wasn’t “collect text input,” it was “make someone discover what they think by doing something, not by typing it.” That’s a different design problem: the mechanic has to generate the content, not just decorate a text field.

What I tried

First pass: four buttons per step, tap one, watch a sentence assemble from your picks. Better than a form, still reads as a quiz — click, click, click, done. There’s no tension, no timing, nothing that feels like play instead of selection.

Second pass: keep the four-step structure, but replace “tap a button” with “stop a spinning reel.” Each step becomes a slot machine window cycling through the same option pool at ~85ms per frame, blurred and jittering to sell motion:

spinTimer = setInterval(() => {
  currentDisplayIndex = (currentDisplayIndex + 1) % opts.length;
  slotText.textContent = opts[currentDisplayIndex];
}, 85);

Tapping the window mid-spin locks in whatever’s showing. Don’t tap in time and it auto-stops after 2.6s. That single change — reaction instead of selection — is what made it feel like a machine instead of a menu.

Four reels build a mad-libs sentence: how the AI wins, who it gets first, when, and the ironic twist. Same options every time, but 4⁴ = 256 possible sentences, and which one you get depends on your timing, not a dropdown you scanned and picked.

What actually worked

Two ideas ended up doing most of the work:

Outcome-driven scoring, not free-form input scoring. Once the sentence is built, you play one hand of blackjack against a dealer AI. The hand’s outcome — blackjack, win, push, bust, dealer blackjack — maps to a rarity tier (Common → Rare → Legendary → Mythic), and a deterministic hash of your reel picks gives you a specific percentage inside that tier’s range:

function hashScore(picksArr) {
  const str = picksArr.join('-');
  let h = 0;
  for (let i = 0; i < str.length; i++) {
    h = (h << 5) - h + str.charCodeAt(i);
    h |= 0;
  }
  return Math.abs(h) % 100;
}

Same picks always produce the same percentage — it’s not random noise on refresh — but the tier is earned by actually playing a hand, not by what you typed. That’s the “discover what you want by doing something” principle applied twice: once to build the sentence, once to decide how good it is.

Canvas certificate as the actual deliverable. The shareable output isn’t a screenshot of the page, it’s a purpose-built 1080×1080 canvas drawn from scratch — dotted background, rarity banner, big percentage, wrapped prophecy text, >GLB_ sign-off — then exported via canvas.toDataURL('image/png') and handed to an <a download>. Nothing to screenshot badly, nothing that looks like a UI mid-interaction.

The whole thing is one HTML file: inline CSS, inline JS, Google Fonts for type, zero build tooling. Easy to drop into public/ on any static site and iframe it, or open directly.

Key takeaways


17 Sep 2026 · #canvas #javascript #no-build

GLB_


Share this post:

Related Posts


Previous Post
Tuva's encounter grouper only groups claims. Here's what happens to your ADT encounters
Next Post
Full risk vs non-risk contracts: not the same axis as risk adjustment