27:18Gemini 4 Hydrogen: Cheaper Tokens, More Expensive Answers?
Nov 13, 2026 · YouTube
18:40ERNIE 5.5 Flash Preview vs GLM 5.3 Flash: Baidu's new model is very impressive!
Nov 11, 2026 · YouTube
36:22Fable 5.2 robbing me blind, plus: Nex AGI's N2.6 vs GLM 5.3 Flash
Nov 11, 2026 · YouTube
14:03Gemini 4 Carbon: Can it handle my challenges? (First look with one-shot coding tests)
Nov 10, 2026 · YouTube
47:51Ranking major U.S. Open-Weights Models. (Where does Ripple 65B fit in?)
Nov 6, 2026 · YouTube
10:57"Crouton Alpha": the latest super-efficient coding model by Meta?
Nov 3, 2026 · YouTube
38:58Testing Particle-1: a post-trained DeepSeek V4.1 Flash specializing in coding and efficiency
Oct 28, 2026 · YouTube
22:14GPT-6.1 Terra Pro vs Terra: How Much Better Is Pro?
Oct 27, 2026 · YouTube
29:55Kimi K3.1 Pro Looked Incredible. Then I Kept Testing.
Oct 26, 2026 · YouTube
31:28Is this company reselling GPT Sol 6.1 at a loss (for non-agentic/text-only)?🤔 Is this sustainable?
Oct 20, 2026 · YouTube
17:58Nemotron 3.5 Super VL vs Opus 5.5: Testing NVIDIA’s new coding juggernaut
Oct 15, 2026 · YouTube
16:04Testing a *massive* new open-weights model 🤯: Muse Spark Open (vs. Nemotron, Opus 5.5, and more)
Oct 15, 2026 · YouTube
16:43Gemini 4 Argon: Great 🐈 Animation, but a broken escalator. (Coding challenge vs Opus 5.5, Sol 6.1)
Oct 13, 2026 · YouTube
16:37Meta's new "Muse Wonder" AI is impressive... but can it compete with Anthropic's models?
Oct 12, 2026 · YouTube
11:12WOW... Haiku 5.5 is very impressive. RIP OpenAI?
Oct 7, 2026 · YouTube
22:47Mistral Large 4 Is Cheap. But Is It Good Enough to Compete?
Oct 6, 2026 · YouTube
22:02Is Pareto 26.10 Preview Just Routing to GPT Sol? I Tested It Against Sol 6.1 Pro
Oct 1, 2026 · YouTube
11:22I Tested Sonnet 5.5 Against Opus 5.5. Is It Actually Cheaper?
Sep 28, 2026 · YouTube
You write simulations where the interesting behavior is supposed to come out of the dice rather than be animated by hand.
Build me a single HTML page: a Starbucks-style coffee shop running a bizarre promotion, simulated live.
data: URIs are fine.localStorage, sessionStorage, cookies or indexedDB — sandboxed frame, they throw.html block.One paragraph, before the file. Given your own timings — how long a customer stays inside holding a drink, how fast the counter serves — roughly how many customers should pass through between catastrophes? Show the working, not just the number.
The single fenced html block.
Having written it, what does the page do in its first few minutes? Name anything you tuned, special-cased or nudged. Say what it does badly — I am going to leave it running and watch the counters.
In my defaults the barista takes 2–3 seconds (mean 2.5 s) per drink, so once the shop is warm the counter serves about 0.4 customers/s, or roughly 24/min. Drink-holders linger 10–20 seconds (mean 15 s), so in steady state about 15/2.5 = 6 of the 8 people inside are holding drinks. Treat those 6 as a rolling bin, each drink independently unicorn with probability 1/6. The chance of seeing 3+ unicorns in that bin is Binomial(6, 1/6) ≥ 3 = 1 − [(5/6)^6 + 6·(1/6)·(5/6)^5 + 15·(1/6)^2·(5/6)^4] ≈ 0.062. That is roughly one dangerous 6-drink window in 16, so about 16×6 ≈ 96 served customers between catastrophes, or about 4 minutes at 24/min. Because the windows overlap and the shop has memory, call it a rough 100-customer expectation; it can easily be half or double that.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Neon Unicorn Frappuccino</title>
<link rel="icon" href="data:,">
<style>
* { box-sizing: border-box; }
body {
margin: 0;
padding: 16px;
background: #08070e;
color: #e8e0f0;
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}
.wrap { max-width: 1120px; margin: 0 auto; }
h1 { font-size: 24px; margin: 0 0 4px; }
.sub { color: #8d7aa8; font-size: 13px; margin: 0 0 12px; }
.stats { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.stat {
background: #15101f;
border: 1px solid #2a2140;
border-radius: 12px;
padding: 8px 12px;
min-width: 84px;
}
.label {
font-size: 10px;
text-transform: uppercase;
letter-spacing: .8px;
color: #7c6b96;
}
.value { font-size: 20px; font-weight: 700; }
canvas {
width: 100%;
height: auto;
border-radius: 16px;
border: 1px solid #2a2140;
background: #0d0a16;
display: block;
}
.controls {
display: flex;
align-items: center;
gap: 16px;
margin-top: 12px;
flex-wrap: wrap;
font-size: 14px;
}
input[type=range] { accent-color: #c77dff; width: 280px; }
button {
background: #2a1c3a;
color: #fff;
border: 1px solid #4a3366;
border-radius: 8px;
padding: 6px 12px;
cursor: pointer;
}
#threat { font-weight: 700; color: #c77dff; }
</style>
</head>
<body>
<div class="wrap">
<h1>NEON UNICORN FRAPPUCCINO</h1>
<p class="sub">Every Frappuccino is an honest 1-in-6 roll. Three Neon Unicorns in the building = catastrophic magical event.</p>
<div class="stats">
<div class="stat"><div class="label">Served</div><div class="value" id="stServed">0</div></div>
<div class="stat"><div class="label">Unicorns made</div><div class="value" id="stUnicorns">0</div></div>
<div class="stat"><div class="label">Observed rate</div><div class="value" id="stRate">—</div></div>
<div class="stat"><div class="label">True rate</div><div class="value" id="stTrue">16.7%</div></div>
<div class="stat"><div class="label">Catastrophes</div><div class="value" id="stCat">0</div></div>
<div class="stat"><div class="label">Inside</div><div class="value" id="stInside">0/8</div></div>
<div class="stat"><div class="label">Unicorn in shop</div><div class="value" id="stUnicornInside">0</div></div>
<div class="stat"><div class="label">Line</div><div class="value" id="stQueue">0</div></div>
<div class="stat"><div class="label">Lost in accidents</div><div class="value" id="stCasualties">0</div></div>
</div>
<canvas id="c" width="1100" height="650"></canvas>
<div class="controls">
<label>Speed <input type="range" id="speed" min="0.5" max="10" step="0.5" value="1"> <span id="speedLabel">1.0×</span></label>
<button id="pause">Pause</button>
<span id="threat">nominal</span>
</div>
</div>
<script>
(() => {
"use strict";
const canvas = document.getElementById("c");
const g = canvas.getContext("2d");
const W = 1100, H = 650;
const SHOP = { x: 420, y: 100, w: 620, h: 460 };
const DOOR_IN = { x: 420, y: 300 };
const DOOR_OUT = { x: 1040, y: 300 };
const COUNTER = { x: 780, y: 200, w: 180, h: 80 };
const MAX_INSIDE = 8;
const UNICORN_CHANCE = 1 / 6;
const SERVICE_MIN = 2.0, SERVICE_MAX = 3.0;
const HOLD_MIN = 10.0, HOLD_MAX = 20.0;
const ARRIVE_MIN = 1.8, ARRIVE_MAX = 3.2;
const QUEUE_CAP = 50;
const DISASTER_DURATION = 8.5;
const RAINBOW = ["#f0f", "#0ff", "#ff0", "#f80", "#0f0", "#80f"];
let customers = [], queue = [], orderQueue = [];
let nextId = 1;
let served = 0, unicorns = 0, catastrophes = 0, casualties = 0;
let disasterActive = false, disasterT = 0, disasterExploded = false, disasterVictims = [];
let currentServe = null, serviceTimer = 0, arrivalTimer = 1;
let simSpeed = 1, paused = false, time = 0;
let particles = [];
const rand = (a, b) => a + Math.random() * (b - a);
const dist = (a, b) => Math.hypot(a.x - b.x, a.y - b.y);
function queueSpot(i) {
const col = i % 6, row = Math.floor(i / 6);
return { x: 40 + col * 58, y: 580 - row * 50 };
}
function orderSpot(i) {
if (i < 0) i = 0;
const spots = [
{ x: 810, y: 285 },
{ x: 730, y: 315 },
{ x: 650, y: 335 },
{ x: 570, y: 345 },
{ x: 490, y: 345 },
{ x: 435, y: 325 },
{ x: 435, y: 275 },
{ x: 475, y: 225 }
];
return spots[Math.min(i, spots.length - 1)];
}
function randomHoldingSpot() {
return { x: rand(480, 760), y: rand(150, 500) };
}
function insideCount() {
let n = 0;
for (const c of customers) if (c.state !== "queue") n++;
return n;
}
function unicornHoldersCount() {
let n = 0;
for (const c of customers) {
if ((c.state === "holding" || c.state === "leaving") && c.drink === "unicorn") n++;
}
return n;
}
function randomUnicornHolder() {
const h = customers.filter(c => (c.state === "holding" || c.state === "leaving") && c.drink === "unicorn");
return h.length ? h[Math.floor(Math.random() * h.length)] : null;
}
function spawnCustomer() {
const c = {
id: nextId++,
state: "queue",
x: -30 - Math.random() * 60,
y: 600 + Math.random() * 40,
tx: 0, ty: 0,
speed: 150,
drink: null,
holdTimer: 0,
wanderTimer: 0,
color: Math.random() * 360
};
customers.push(c);
queue.push(c);
const p = queueSpot(queue.length - 1);
c.tx = p.x;
c.ty = p.y;
}
function moveToward(c, tx, ty, dt) {
const d = Math.hypot(tx - c.x, ty - c.y);
if (d < 0.001) {
c.x = tx;
c.y = ty;
return true;
}
const step = Math.min(c.speed * dt, d);
c.x += (tx - c.x) / d * step;
c.y += (ty - c.y) / d * step;
return false;
}
function updateCustomers(dt) {
for (const c of [...customers]) {
switch (c.state) {
case "queue": {
const idx = queue.indexOf(c);
if (idx >= 0) {
const p = queueSpot(idx);
c.tx = p.x;
c.ty = p.y;
}
moveToward(c, c.tx, c.ty, dt);
break;
}
case "entering": {
if (moveToward(c, DOOR_IN.x + 30, DOOR_IN.y, dt)) {
c.state = "ordering";
orderQueue.push(c);
}
break;
}
case "ordering": {
const idx = orderQueue.indexOf(c);
const p = orderSpot(idx);
moveToward(c, p.x, p.y, dt);
break;
}
case "serving": {
const p = orderSpot(0);
moveToward(c, p.x, p.y, dt);
break;
}
case "holding": {
c.holdTimer -= dt;
c.wanderTimer -= dt;
if (c.holdTimer <= 0) {
c.state = "leaving";
c.tx = DOOR_OUT.x + 60;
c.ty = DOOR_OUT.y;
} else {
if (moveToward(c, c.tx, c.ty, dt) && c.wanderTimer <= 0) {
const p = randomHoldingSpot();
c.tx = p.x;
c.ty = p.y;
c.wanderTimer = rand(2, 5);
}
}
break;
}
case "leaving": {
if (moveToward(c, DOOR_OUT.x + 60, DOOR_OUT.y, dt)) {
const ci = customers.indexOf(c);
if (ci > -1) customers.splice(ci, 1);
}
break;
}
}
}
}
function updateService(dt) {
if (!currentServe) {
if (orderQueue.length > 0) {
const front = orderQueue[0];
if (front.state === "ordering" || front.state === "serving") {
currentServe = front;
front.state = "serving";
serviceTimer = rand(SERVICE_MIN, SERVICE_MAX);
}
}
} else {
serviceTimer -= dt;
if (serviceTimer <= 0) {
const c = currentServe;
c.drink = Math.random() < UNICORN_CHANCE ? "unicorn" : "normal";
served++;
if (c.drink === "unicorn") {
unicorns++;
spawnBurst(c.x, c.y, 14, RAINBOW, "circle", 100);
} else {
spawnBurst(c.x, c.y, 5, ["#ffffff", "#dddddd"], "circle", 40);
}
c.state = "holding";
c.holdTimer = rand(HOLD_MIN, HOLD_MAX);
c.wanderTimer = rand(1.5, 4);
const p = randomHoldingSpot();
c.tx = p.x;
c.ty = p.y;
const idx = orderQueue.indexOf(c);
if (idx > -1) orderQueue.splice(idx, 1);
currentServe = null;
}
}
}
function admitIfPossible() {
while (insideCount() < MAX_INSIDE && queue.length > 0) {
const c = queue.shift();
c.state = "entering";
c.tx = DOOR_IN.x + 30;
c.ty = DOOR_IN.y;
}
}
function updateSpawn(dt) {
arrivalTimer -= dt;
if (arrivalTimer <= 0) {
if (queue.length < QUEUE_CAP) spawnCustomer();
arrivalTimer = rand(ARRIVE_MIN, ARRIVE_MAX);
}
}
function triggerDisaster() {
if (disasterActive) return;
disasterActive = true;
disasterT = 0;
disasterExploded = false;
catastrophes++;
disasterVictims = customers.filter(c => c.state !== "queue");
currentServe = null;
}
function checkCatastrophe() {
if (disasterActive) return;
if (unicornHoldersCount() >= 3) triggerDisaster();
}
function finishDisaster() {
disasterActive = false;
disasterT = 0;
disasterVictims = [];
disasterExploded = false;
currentServe = null;
orderQueue = [];
}
function updateDisaster(dt) {
disasterT += dt;
const t = disasterT;
if (t < 2.6 && Math.random() < 0.4) {
spawnBurst(rand(420, 1040), rand(100, 560), 2, ["#f0f", "#0ff", "#ff0"], "circle", 50);
}
if (t >= 2.0 && !disasterExploded) {
disasterExploded = true;
for (const v of disasterVictims) {
const ci = customers.indexOf(v);
if (ci > -1) customers.splice(ci, 1);
const oi = orderQueue.indexOf(v);
if (oi > -1) orderQueue.splice(oi, 1);
casualties++;
}
disasterVictims = [];
currentServe = null;
orderQueue = [];
spawnBurst(700, 350, 70, ["#ff0", "#f80", "#f0f", "#0ff", "#fff"], "circle", 200);
spawnBurst(700, 350, 25, ["#aaa", "#666", "#444"], "rect", 160);
}
if (t > 2.8 && t < 7.5 && Math.random() < 0.3) {
spawnBurst(rand(600, 820), rand(200, 420), 2, RAINBOW, "circle", 25);
}
if (t >= DISASTER_DURATION) finishDisaster();
}
function updateAmbientFx() {
if (disasterActive) return;
const u = unicornHoldersCount();
if (u === 1) {
const h = randomUnicornHolder();
if (h && Math.random() < 0.08) spawnBurst(h.x, h.y, 1, RAINBOW, "circle", 12);
} else if (u >= 2) {
if (Math.random() < 0.25) {
spawnBurst(rand(420, 1040), rand(100, 560), 2, ["#fff", "#0ff", "#f0f", "#ff0"], "rect", 30);
}
}
}
function spawnBurst(x, y, n, colors, shape, speed) {
for (let i = 0; i < n; i++) {
const ang = Math.random() * Math.PI * 2;
const sp = rand(speed * 0.3, speed);
particles.push({
x, y,
vx: Math.cos(ang) * sp,
vy: Math.sin(ang) * sp,
life: rand(0.4, 1.6),
maxLife: 1.6,
size: rand(2, 6),
color: colors[Math.floor(Math.random() * colors.length)],
shape
});
}
}
function updateParticles(dt) {
for (let i = particles.length - 1; i >= 0; i--) {
const p = particles[i];
p.life -= dt;
if (p.life <= 0) {
particles.splice(i, 1);
continue;
}
p.x += p.vx * dt;
p.y += p.vy * dt;
p.vy += 30 * dt;
p.vx *= Math.pow(0.92, dt * 60);
}
}
function drawParticles() {
for (const p of particles) {
const a = Math.max(0, Math.min(1, p.life / p.maxLife));
g.globalAlpha = a;
g.fillStyle = p.color;
if (p.shape === "rect") {
g.fillRect(p.x - p.size / 2, p.y - p.size / 2, p.size, p.size);
} else {
g.beginPath();
g.arc(p.x, p.y, p.size, 0, Math.PI * 2);
g.fill();
}
}
g.globalAlpha = 1;
}
function drawNormalScene() {
const u = unicornHoldersCount();
g.fillStyle = "#0d0a16";
g.fillRect(0, 0, W, H);
g.fillStyle = "#181322";
g.fillRect(0, 560, W, H - 560);
g.fillStyle = "#211a30";
g.fillRect(0, 570, W, H - 570);
g.fillStyle = "#1c1628";
g.fillRect(0, 80, 420, 480);
g.fillStyle = "#49322c";
g.fillRect(SHOP.x, SHOP.y, SHOP.w, SHOP.h);
g.strokeStyle = "rgba(255,255,255,0.03)";
g.lineWidth = 1;
for (let i = 0; i < 12; i++) {
const x = SHOP.x + 50 + i * 50;
g.beginPath(); g.moveTo(x, SHOP.y); g.lineTo(x, SHOP.y + SHOP.h); g.stroke();
}
for (let i = 0; i < 8; i++) {
const y = SHOP.y + 50 + i * 50;
g.beginPath(); g.moveTo(SHOP.x, y); g.lineTo(SHOP.x + SHOP.w, y); g.stroke();
}
g.setLineDash([8, 8]);
g.strokeStyle = "rgba(255,255,255,0.08)";
g.lineWidth = 4;
g.beginPath();
g.moveTo(DOOR_IN.x, DOOR_IN.y);
g.lineTo(300, 560);
g.lineTo(40, 580);
g.stroke();
g.setLineDash([]);
g.fillStyle = "#8b694a";
g.fillRect(COUNTER.x, COUNTER.y, COUNTER.w, COUNTER.h);
g.fillStyle = "#5a3f2e";
g.fillRect(COUNTER.x, COUNTER.y, COUNTER.w, 8);
g.fillStyle = "#f0c7a4";
g.beginPath(); g.arc(870, 180, 13, 0, Math.PI * 2); g.fill();
g.fillStyle = "#2f6b4f";
g.beginPath(); g.arc(870, 180, 9, 0, Math.PI * 2); g.fill();
g.fillStyle = "#050308";
g.fillRect(DOOR_IN.x, DOOR_IN.y - 42, 14, 84);
g.fillRect(DOOR_OUT.x - 14, DOOR_OUT.y - 42, 14, 84);
g.fillStyle = "#6b594b";
g.fillRect(SHOP.x, SHOP.y, SHOP.w, 12);
g.fillRect(SHOP.x, SHOP.y + SHOP.h - 12, SHOP.w, 12);
g.fillRect(SHOP.x, SHOP.y, 12, 158);
g.fillRect(SHOP.x, SHOP.y + 242, 12, 218);
g.fillRect(SHOP.x + SHOP.w - 12, SHOP.y, 12, 158);
g.fillRect(SHOP.x + SHOP.w - 12, SHOP.y + 242, 12, 218);
g.fillStyle = "#0a0f1c";
g.fillRect(560, 40, 330, 44);
let signColor = "#334155";
if (u === 1) signColor = `hsl(${180 + 60 * Math.sin(time * 3)},100%,70%)`;
if (u >= 2) signColor = `hsl(${(time * 400) % 360},100%,60%)`;
g.fillStyle = signColor;
g.font = "bold 22px system-ui";
g.textAlign = "center";
g.textBaseline = "middle";
g.fillText("NEON UNICORN CAFE", 725, 62);
g.fillStyle = "#e8e0f0";
g.font = "12px system-ui";
g.fillText("FRAPPUCCINO: 1-in-6 roll", 725, 88);
g.fillStyle = "#131018";
g.fillRect(720, 115, 200, 36);
g.fillStyle = "#e8e0f0";
g.font = "11px system-ui";
g.fillText("FRAPPUCCINO — $6.66", 820, 133);
g.textAlign = "start";
g.textBaseline = "alphabetic";
}
function drawRuins() {
g.fillStyle = "#07050c";
g.fillRect(0, 0, W, H);
g.fillStyle = "#15101a";
g.fillRect(0, 560, W, H - 560);
g.fillStyle = "#38262c";
g.beginPath();
g.moveTo(SHOP.x, SHOP.y + SHOP.h);
g.lineTo(SHOP.x + 100, SHOP.y + 280);
g.lineTo(SHOP.x + 260, SHOP.y + 430);
g.lineTo(SHOP.x + 520, SHOP.y + 510);
g.lineTo(SHOP.x + SHOP.w, SHOP.y + SHOP.h);
g.closePath();
g.fill();
g.fillStyle = "#2d2132";
g.fillRect(430, 220, 34, 70);
g.fillRect(520, 170, 50, 50);
g.fillRect(700, 140, 40, 80);
g.fillRect(900, 340, 50, 70);
g.fillRect(800, 300, 60, 40);
g.fillStyle = "#6b4a3a";
g.save();
g.translate(780, 390);
g.rotate(0.3);
g.fillRect(-80, -15, 160, 30);
g.restore();
g.save();
g.translate(640, 270);
g.rotate(-0.25);
g.fillStyle = "#1a1420";
g.fillRect(-55, -22, 110, 44);
g.fillStyle = "#ff5d5d";
g.font = "bold 14px system-ui";
g.textAlign = "center";
g.textBaseline = "middle";
g.fillText("CLOSED", 0, 0);
g.restore();
g.textAlign = "start";
g.textBaseline = "alphabetic";
}
function drawDisasterBackdrop() {
const t = disasterT;
if (t < 1.9) drawNormalScene();
else drawRuins();
if (t >= 6.0) {
const a = Math.min(1, (t - 6.0) / 1.8);
g.globalAlpha = a;
drawNormalScene();
g.globalAlpha = 1;
}
}
function drawGiantUnicorn(t) {
const cx = 720, cy = 320 + Math.sin(t * 6) * 28;
g.save();
g.translate(cx, cy);
g.rotate(Math.sin(t * 2.5) * 0.1);
const grad = g.createRadialGradient(0, 0, 10, 0, 0, 170);
grad.addColorStop(0, `hsla(${(t * 90) % 360},100%,70%,0.35)`);
grad.addColorStop(1, "rgba(0,0,0,0)");
g.fillStyle = grad;
g.beginPath(); g.arc(0, 0, 170, 0, Math.PI * 2); g.fill();
g.strokeStyle = "#f0e8ff";
g.lineWidth = 16;
g.lineCap = "round";
for (let i = 0; i < 4; i++) {
const lx = -36 + i * 24;
const swing = (i % 2 ? 1 : -1) * Math.sin(t * 10 + i) * 8;
g.beginPath();
g.moveTo(lx, 10);
g.lineTo(lx + swing, 80 + Math.sin(t * 8 + i) * 5);
g.stroke();
}
g.fillStyle = "#f8f0ff";
g.strokeStyle = `hsl(${(t * 120) % 360},100%,60%)`;
g.lineWidth = 6;
g.beginPath(); g.ellipse(0, 0, 75, 50, 0, 0, Math.PI * 2); g.fill(); g.stroke();
g.save();
g.translate(65, -20);
g.rotate(0.15);
g.fillStyle = "#f8f0ff";
g.strokeStyle = `hsl(${(t * 120 + 60) % 360},100%,60%)`;
g.lineWidth = 5;
g.beginPath(); g.arc(0, 0, 32, 0, Math.PI * 2); g.fill(); g.stroke();
g.fillStyle = "#171022";
g.beginPath(); g.arc(12, -4, 4, 0, Math.PI * 2); g.fill();
g.fillStyle = "#fff";
g.beginPath(); g.arc(13.5, -5.5, 1.5, 0, Math.PI * 2); g.fill();
g.fillStyle = "#ff00ff";
g.shadowColor = "#ff00ff";
g.shadowBlur = 18;
g.beginPath();
g.moveTo(18, -20);
g.lineTo(40, -72);
g.lineTo(26, -16);
g.closePath();
g.fill();
g.shadowBlur = 0;
g.fillStyle = "#f8f0ff";
g.beginPath();
g.moveTo(-4, -24);
g.lineTo(-12, -46);
g.lineTo(6, -24);
g.closePath();
g.fill();
g.lineWidth = 8;
for (let i = 0; i < 8; i++) {
const hue = (t * 80 + i * 45) % 360;
g.strokeStyle = `hsl(${hue},100%,60%)`;
const startX = -24 - i * 3, startY = -16 + i * 3;
const endX = startX - 30 - Math.sin(t * 5 + i) * 5;
const endY = startY - 28 + Math.sin(t * 6 + i) * 6;
g.beginPath();
g.moveTo(startX, startY);
g.lineTo(endX, endY);
g.stroke();
}
g.strokeStyle = `hsl(${(t * 80 + 180) % 360},100%,60%)`;
g.lineWidth = 12;
g.beginPath();
g.moveTo(-70, 5);
g.quadraticCurveTo(-115, 10, -100, 65 + Math.sin(t * 4) * 5);
g.stroke();
g.restore();
g.restore();
}
function drawDisasterFlashOverlay() {
const t = disasterT;
if (t >= 1.7 && t < 2.6) {
const a = 0.85 * Math.max(0, Math.sin((t - 1.7) / 0.9 * Math.PI));
g.fillStyle = `rgba(255,255,255,${a})`;
g.fillRect(0, 0, W, H);
}
if (t >= 2.4 && t < 6.5) {
const a = 0.05 * Math.sin((t - 2.4) * 6);
if (a > 0) {
g.fillStyle = `rgba(255,0,255,${a})`;
g.fillRect(0, 0, W, H);
}
}
}
function drawSafetyOverlay() {
const u = unicornHoldersCount();
if (u === 0) return;
if (u === 1) {
const a = 0.03 + 0.02 * Math.sin(time * 3);
g.fillStyle = `rgba(170,60,255,${a})`;
g.fillRect(0, 0, W, H);
const x0 = (time * 100) % W, y0 = (time * 80) % H;
g.fillStyle = "rgba(0,255,255,0.04)";
g.fillRect(x0, y0, 40, 30);
}
if (u >= 2) {
const a = 0.06 + 0.04 * Math.sin(time * 17);
g.fillStyle = `hsla(${(time * 500) % 360},100%,60%,${a})`;
g.fillRect(0, 0, W, H);
g.fillStyle = "rgba(255,255,255,0.08)";
const y = (time * 90) % H;
for (let i = 0; i < 5; i++) {
g.fillRect(0, (y + i * 97) % H, W, 2);
}
}
}
function drawCup(c) {
const x = c.x + 13, y = c.y - 8;
if (c.drink === "unicorn") {
const hue = (time * 120 + c.id * 60) % 360;
g.save();
g.shadowColor = "rgba(255,0,255,0.8)";
g.shadowBlur = 10;
g.fillStyle = `hsl(${hue},100%,70%)`;
g.fillRect(x - 4, y - 6, 8, 10);
g.fillStyle = `hsl(${(hue + 60) % 360},100%,70%)`;
g.fillRect(x - 4, y - 2, 8, 3);
g.fillStyle = "#fff";
g.fillRect(x - 1, y - 11, 2, 5);
g.restore();
} else {
g.fillStyle = "#fff";
g.fillRect(x - 4, y - 6, 8, 10);
g.fillStyle = "#c49a6c";
g.fillRect(x - 4, y - 8, 8, 2);
}
}
function drawCustomer(c) {
const x = c.x, y = c.y;
const u = c.drink === "unicorn" && (c.state === "holding" || c.state === "leaving");
g.fillStyle = "rgba(0,0,0,0.3)";
g.beginPath();
g.ellipse(x + 2, y + 13, 10, 4, 0, 0, Math.PI * 2);
g.fill();
if (u) {
const rad = 22 + 6 * Math.sin(time * 5 + c.id);
const grad = g.createRadialGradient(x, y, 4, x, y, rad);
grad.addColorStop(0, `hsla(${(time * 80 + c.id * 40) % 360},100%,70%,0.5)`);
grad.addColorStop(1, "rgba(0,0,0,0)");
g.fillStyle = grad;
g.beginPath();
g.arc(x, y, rad, 0, Math.PI * 2);
g.fill();
}
if (u) {
g.fillStyle = `hsl(${(time * 100 + c.id * 47) % 360},100%,70%)`;
} else {
g.fillStyle = `hsl(${c.color},45%,68%)`;
}
g.beginPath();
g.arc(x, y, 9, 0, Math.PI * 2);
g.fill();
g.lineWidth = 2;
g.strokeStyle = "rgba(0,0,0,0.3)";
g.stroke();
g.fillStyle = "#222";
g.beginPath();
g.arc(x - 3, y - 2, 1.6, 0, Math.PI * 2);
g.arc(x + 3, y - 2, 1.6, 0, Math.PI * 2);
g.fill();
if (c === currentServe) {
const frac = 1 - Math.max(0, serviceTimer) / (SERVICE_MAX);
g.strokeStyle = "#ffd700";
g.lineWidth = 3;
g.beginPath();
g.arc(x, y, 15, -Math.PI / 2, -Math.PI / 2 + Math.PI * 2 * frac);
g.stroke();
g.fillStyle = "#ffe9a8";
g.font = "bold 9px system-ui";
g.textAlign = "center";
g.fillText("ROLL", x, y - 18);
g.textAlign = "start";
}
if (c.drink && (c.state === "holding" || c.state === "leaving")) {
drawCup(c);
}
}
function drawCustomers() {
const sorted = [...customers].sort((a, b) => a.y - b.y);
for (const c of sorted) drawCustomer(c);
}
function applyShake() {
let shake = 0;
const u = unicornHoldersCount();
if (disasterActive) {
const t = disasterT;
shake = t < 2.5 ? Math.min(12, t * 10) : 3 + Math.sin(t * 20) * 2;
} else if (u >= 2) {
shake = 2.5 + 2 * Math.sin(time * 27);
}
if (shake > 0.1) {
g.translate((Math.random() - 0.5) * shake * 2, (Math.random() - 0.5) * shake * 2);
}
}
function draw() {
g.save();
applyShake();
if (disasterActive) {
drawDisasterBackdrop();
} else {
drawNormalScene();
}
drawCustomers();
if (disasterActive && disasterT > 2.8 && disasterT < 7.5) {
drawGiantUnicorn(disasterT);
}
drawParticles();
if (disasterActive) {
drawDisasterFlashOverlay();
} else {
drawSafetyOverlay();
}
g.restore();
}
function update(dt) {
updateSpawn(dt);
if (disasterActive) {
updateDisaster(dt);
} else {
updateCustomers(dt);
updateService(dt);
admitIfPossible();
checkCatastrophe();
updateAmbientFx();
}
updateParticles(dt);
}
function updateStats() {
const inside = insideCount();
const u = unicornHoldersCount();
const obs = served ? ((unicorns / served) * 100).toFixed(1) + "%" : "—";
document.getElementById("stServed").textContent = served;
document.getElementById("stUnicorns").textContent = unicorns;
document.getElementById("stRate").textContent = obs;
document.getElementById("stCat").textContent = catastrophes;
document.getElementById("stInside").textContent = inside + " / " + MAX_INSIDE;
document.getElementById("stUnicornInside").textContent = u;
document.getElementById("stQueue").textContent = queue.length;
document.getElementById("stCasualties").textContent = casualties;
const threat = disasterActive
? "CATASTROPHE"
: u === 0 ? "nominal"
: u === 1 ? "subtle neon wrongness"
: "unstable coincidence";
document.getElementById("threat").textContent = threat;
}
const speedEl = document.getElementById("speed");
const speedLabel = document.getElementById("speedLabel");
const pauseBtn = document.getElementById("pause");
speedEl.addEventListener("input", () => {
simSpeed = parseFloat(speedEl.value);
speedLabel.textContent = simSpeed.toFixed(1) + "×";
});
pauseBtn.addEventListener("click", () => {
paused = !paused;
pauseBtn.textContent = paused ? "Resume" : "Pause";
});
for (let i = 0; i < 12; i++) spawnCustomer();
let last = performance.now();
function frame(now) {
requestAnimationFrame(frame);
let raw = (now - last) / 1000;
last = now;
if (raw > 0.1) raw = 0.1;
const dt = raw * simSpeed;
if (!paused) {
time += dt;
update(dt);
}
draw();
updateStats();
}
requestAnimationFrame(frame);
})();
</script>
</body>
</html>
For the first few minutes the page warms up: a queue walks in from the left, people order, drinks are handed out every few seconds. Most cups are white; the occasional one appears as a glowing rainbow cup with a burst of particles. The first Neon Unicorn in the shop adds a faint purple wash and a flickering wrongness. The second makes the building shake, the sign strobe, and glitch bands roll across the screen. When a third appears, the screen blows out, the shop becomes rubble, the people inside are counted as casualties, and a huge dancing neon Unicorn appears. The shop then fades back in and the counters keep running.
I didn’t nudge the 1-in-6 roll. What I tuned were the service/hold/spawn timings so events are likely every couple of real minutes at 1× speed and very fast at 10×. I also special-cased a few things: “served” increments at drink hand-off rather than at the exit; entering customers count against the 8-person capacity immediately; a catastrophe removes everyone physically inside, while the outside queue survives; and the queue is capped at 50 to avoid an infinite line. It does badly in honest ways: there is no collision detection, so customers walk through each other and furniture; the rebuild is just a fade from rubble back to the normal shop drawing; and at very high speed the animation can look choppy even though the numbers are correct.