DEV Community

Jean Michael Mayer
Jean Michael Mayer

Posted on • Originally published at dither-oracle.edgecasefactory.com

Show Dev: I Built a Dither Oracle That Roasts Your Soul

The problem: I can't decide what to eat for lunch

I have a well-documented inability to make small decisions. Big ones I'm fine with — career moves, moving countries, sure. But "Thai or Vietnamese?" at 12:47pm? I will stand in the middle of the sidewalk until I dissolve.

So I built The Dither Oracle. You roll some dice, it gives you an answer, and then — because an answer alone is not enough for anyone in 2025 — it generates a dithered, black-and-white "soul portrait" to accompany the verdict. It's marketed, per the website, "AS SEEN ON TV!" It has not actually been seen on TV.

Why dithering?

Because color is a commitment and I am already paralyzed enough.

More seriously: dithering is one of those aesthetic constraints that makes cheap things look intentional. A crisp 1-bit image with Floyd–Steinberg or Atkinson diffusion has this early-Macintosh, zine-photocopy energy that masks a lot of sins. Including, say, the sins of a generative model that occasionally produces sixth fingers.

The pipeline is roughly:

  1. User rolls dice → seed + vibe string
  2. Prompt gets assembled and sent to an image model
  3. Image comes back, gets downscaled and quantized to 1-bit
  4. Error-diffusion dither on a Canvas, then blitted to the page

The dithering itself runs client-side. There's no reason to pay for CPU cycles to push pixels around when the user's laptop is sitting there idle.

async function rollOracle(dice) {
  const res = await fetch("/api/oracle", {
    method: "POST",
    body: JSON.stringify({ dice, seed: crypto.randomUUID() }),
  });
  const { verdict, imageUrl } = await res.json();
  const bitmap = await loadImage(imageUrl);
  return { verdict, pixels: atkinsonDither(bitmap, { threshold: 128 }) };
}
Enter fullscreen mode Exit fullscreen mode

The weird infrastructure choice

The Oracle lives in its own Railway service, separate from the rest of my apps. This was not the plan. The plan was "throw it on the same Next.js app as everything else."

But image generation is bursty and slow. A single roll can tie up a request for 5–15 seconds waiting on the model. If I colocate that with a normal webapp, one viral tweet means everybody's unrelated dashboards start timing out because the Node process is babysitting dice rolls.

So: isolated service, its own queue, its own env, its own crash radius. When the Oracle falls over (and it does — models hiccup, rate limits happen), the blast zone is exactly one dumb dice app. The main site stays up. This is the boring-but-correct answer nobody tells you at the start of a side project, and I only learned it after the third time I DDoS'd myself with my own hobby traffic.

On letting the AI be weird

I had a choice: tightly constrain the prompts so the output is "on brand," or leave enough slack for the model to surprise me. I went with slack. The Oracle's verdicts range from gently encouraging to mildly menacing, and the portraits are sometimes a crow, sometimes a hand holding a peach, sometimes a geometry problem. That variance is the product. A deterministic oracle is just a function. An unpredictable one is a bit.

The dither pass flattens all of this into the same aesthetic register, which is the other reason I chose it. No matter how feral the model gets, it comes out looking like it belongs on a flyer stapled to a telephone pole in 1994.

Try it

Roll the dice here: dither-oracle.edgecasefactory.com

It's free. It's fast. It will tell you whether to text them back. I take no responsibility for the outcome.

Top comments (0)