A Manifest V3 Chrome extension that redirects the YouTube homepage to any URL you choose — so you skip the recommendations feed without losing search, videos,
A joke calculator that returns a deterministically wrong answer with a straight-faced reason — built as a share machine. Same expression, same wrong answer, forever, in 17 languages, with link previews that unfurl into the joke.
Libraries
Databases
Key Results

Most web toys die in ten seconds because they are random. A calculator that says 2+2=7 for you and 2+2=5 for your friend gives nobody anything to talk about. The whole product is one design decision: the wrongness has to be reproducible. Only then does "Wrongulator says 2+2 is 5" become a shared fact people can link to, screenshot, and argue about — which is the entire growth loop.
That single rule cascades into hard engineering constraints:
/2+2 reproduces the exact result anywhere, instantly, for free.Wrongulator is built as a share machine. A vanilla HTML/CSS/JS frontend sits over a deliberately thin Express server that exists for only three things that genuinely need a server — the share image, per-link social preview data, and the global share counter. Everything that can be a pure client-side function is one, which keeps the whole toy cheap to run and effortless to scale.
The core is a "Wrong Engine" that maps any expression to one reproducible wrong answer plus a deadpan reason. Because it's deterministic, 2+2 returns the same wrong answer for everyone, forever — which is exactly what turns it into a shareable inside joke rather than a forgettable random gag. And because that engine runs entirely in the browser, it's free, works offline, and scales without limit.
For sharing to actually work, a link has to unfurl into the joke. Social crawlers don't run JavaScript, so the server bakes the real wrong answer, its reason, and a matching 1080×1080 share image into the page before any JavaScript executes. The share image is drawn by a single routine that runs identically in the browser and on the server, so the card a user sees is pixel-for-pixel the card that unfurls on social — and because the output is deterministic, it's cached forever without re-rendering.
The same server-side rendering gives it real SEO reach: a curated set of the arithmetic people most often search (2+2, 9+10, 7*8, …) becomes an indexable, programmatic-SEO surface with localized metadata across all 17 languages. The localization is careful about humor — it translates the prose that merely carries the joke, but leaves universal number-memes untranslated because they're funnier as-is, and even adds locale-only memes that fire in a single language. All 17 languages, including RTL Arabic, got a native-speaker review pass.
Finally, a "Hall of Fame" leaderboard tracks the most-shared wrong answers. The client only ever sends the expression, never the answer — the server recomputes the result itself before counting, so the leaderboard can't be gamed. It ships as one Docker container and runs end-to-end with zero infrastructure (an in-memory store stands in when Redis isn't configured).
| Metric | Value |
|---|---|
| Codebase | ~4,350 lines (vanilla JS/HTML, no frontend framework) |
| Wrong Engine | Pure function, FNV-1a hash + mulberry32 PRNG, fully client-side |
| Meme catalogue | 18 entries with magnet-snap radius + 1-in-a-million "accidentally right" |
| Languages | 17, including RTL Arabic, with native meme-slang review |
| Share card | One Canvas routine, rendered identically in browser and on the server |
| OG image cache | Immutable, 1-year — deterministic output never re-renders |
| SEO | Server-side meta + hreflang × 17 + JSON-LD FAQ per expression |
| Hall of Fame | Redis ZSET leaderboard, server-recomputed (spoof-proof), in-memory fallback |
| Runtime deps | 3 (Express, @napi-rs/canvas, ioredis) |
| Deploy | Single Docker container on Coolify, /healthz healthcheck |
Wrongulator is a study in disciplined scope: the funny part is one pure function, and everything around it — sharing, SEO, localization, the leaderboard — is distribution engineered to put that function in front of as many people as possible, as cheaply as possible.
For the technically curious, here is how the core pieces are built.
Math.random()The engine never calls Math.random(). It hashes the normalized expression with FNV-1a, seeds a mulberry32 PRNG, and draws every decision — which wrongness mode fires, which meme it snaps to, which justification line it picks — from that one deterministic stream. Same input, same wrong answer, same reason, forever, on every device.
// public/engine.js — deterministic seeding
function hashStr(s) {
let h = 2166136261 >>> 0; // FNV-1a
for (let i = 0; i < s.length; i++) {
h ^= s.charCodeAt(i);
h = Math.imul(h, 16777619);
}
return h >>> 0;
}
function mulberry32(a) {
return function () {
a |= 0;
a = (a + 0x6d2b79f5) | 0;
let t = Math.imul(a ^ (a >>> 15), 1 | a);
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}The wrong answer isn't arbitrary — it's legibly wrong. The engine has 18 meme entries with a magnet radius: when the real result lands near a meme number, it snaps to it so the joke feels earned (64+5 → 67, "the only correct number"). It also hides a one-in-a-million "accidentally right" easter egg — a rare collectible card that returns the correct answer with a MALFUNCTION reason.
// MAGNET — if the real answer is near a meme, snap to it (feels earned)
let best = null,
bestD = Infinity;
for (const m of MEMES) {
if (m.n == null || !m.magnet || m.n === realRounded || !inLocale(m)) continue;
const d = Math.abs(realRounded - m.n);
if (d > 0 && d <= m.magnet && d < bestD) {
best = m;
bestD = d;
}
}The share card is the unit of virality, so the picture a user shares must be byte-for-byte the picture a crawler unfurls. Rather than maintain two renderers, card-core.js is written against the bare Canvas 2D API and exported as a UMD module — the browser drives it with a real <canvas>, and the server drives the same function with @napi-rs/canvas.
// public/card-core.js — environment-agnostic UMD; same draw() in both runtimes
(function (root, factory) {
if (typeof module === "object" && module.exports) module.exports = factory();
else root.WrongCardCore = factory();
})(typeof self !== "undefined" ? self : this, function () {
// ... drawCard(ctx, { expr, answer, reason, headline, emoji })
});// server.js — the OG endpoint feeds the identical core a node canvas
app.get('/api/og', async (req, res) => {
const canvas = createCanvas(core.W, core.H);
const r = wrongulate(expr, tone, lang);
core.drawCard(canvas.getContext('2d'), { expr, answer: r.answer, reason: r.reason, ... });
res.setHeader('Cache-Control', 'public, immutable, max-age=31536000'); // output is deterministic
res.end(canvas.toBuffer('image/png'));
});Because the output is a pure function of the query, the image is cached immutable for a year — the CDN serves the same wrong card forever without ever re-rendering.
A link to /9+10 is worthless if the unfurl shows a generic splash. So the server computes everything a crawler needs — localized <title>, description, canonical URL, hreflang alternates for every language, OpenGraph/Twitter cards, and JSON-LD — and injects the actual wrong answer into the HTML body. The page has real, indexable content the moment it arrives.
// server.js — JSON-LD FAQ with the real (wrong) answer, per expression
const jsonld = {
"@context": "https://schema.org",
"@type": "FAQPage",
mainEntity: [
{
"@type": "Question",
name: `What is ${disp}?`,
acceptedAnswer: {
"@type": "Answer",
text: `Wrongulator says ${disp} = ${r.answer}. ${r.reason}`,
},
},
],
};A curated sitemap.xml seeds the highest-search expressions (2+2, 9+10, 7*8, …) as a programmatic-SEO surface, each with hreflang alternates across all 17 languages — so the toy ranks for the exact arithmetic people actually type into search.
Naïvely translating everything would flatten the humor. The i18n layer splits the copy: it translates the prose that merely carries the joke (the deadpan reasons, the UI), but leaves the universal memes untranslated (skibidi, Ohio, 6-7, 300, 9+10=21) because they travel as-is and are funnier left alone. It even adds locale-gated memes that only fire in their own language — Thai 555 (= "hahaha"), Japanese 39 (= "thank you"), Finnish 666 (perkele) — each written natively, never translated.
// public/i18n.js — split by what the text DOES
// translate: deadpan/ai reasons, void/malfunction lines, UI prompts
// leave alone: universal number-memes; brand labels ("100% WRONG", "HALL OF FAME")
const MEMES = {
meme555: ['ห้า ห้า ห้า. ตลกจนเครื่องคิดเลขขำ.', ...], // th — only ever shown for th
meme39: ['サンキュー。3-9。電卓からの感謝です。', ...], // ja
meme666: ['PERKELE. Tämä on ainoa oikea vastaus.', ...], // fi
};All 17 languages — including RTL Arabic — got a native-speaker, meme-forward review pass. The OG renderer carries a per-glyph font fallback chain (Russo One for Latin/Cyrillic, Noto faces for Thai/Arabic/Japanese/Korean, Noto Color Emoji) so a mixed string like "サンキュー 🙏" renders fully on the server-drawn card.
The global "most-shared wrong answers" leaderboard lives in a Redis sorted set, with a companion hash holding each entry's { expr, answer, reason }. The trick: the client never sends the answer — it sends only the expression, and the server runs the engine itself before incrementing. You can't inflate a fake wrong answer into the leaderboard.
// server.js — server recomputes the answer, so counts can't be spoofed
async share(expr) {
const k = norm(expr);
await redis.hset(H, k, JSON.stringify(metaFor(expr))); // metaFor() re-runs the engine
return redis.zincrby(Z, 1, k);
}If REDIS_URL is unset, an in-memory implementation with the identical interface takes over — so the app runs end-to-end with npm run dev and zero infrastructure.
AvailableNeed something similar?
I build custom solutions — from APIs to full products. Let's talk about your project.
A Manifest V3 Chrome extension that redirects the YouTube homepage to any URL you choose — so you skip the recommendations feed without losing search, videos,
Developer-first SaaS API for EU VAT validation via VIES with Redis caching, change monitoring, and webhook notifications.
Programmatic SEO with hreflang: how each wrong answer in a viral toy is a server-rendered page with localized meta and JSON-LD, across 17 languages.
Isomorphic canvas rendering: one drawCard() runs in the browser and on the server via @napi-rs/canvas, so the share image and in-app card never drift.