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.
With a 2.8-second counter cadence—1.6 seconds taking the order plus 1.2 seconds “brewing”—and drinks held inside for 8.0 seconds before a brisk walk to the door, each new drink has the previous three drinks still inside and the fourth already gone. A catastrophe therefore occurs when the new drink is unicorn and at least two of the preceding three drinks were unicorn: (1/6 \times [3(1/6)^2(5/6) + (1/6)^3] = 1/81). The expected run is consequently about 81 served customers per catastrophe, or roughly 3 minutes 47 seconds at 1×; at 8×, the service-time portion is about 28 seconds, followed by a deliberately unscaled 7.2-second spectacle. The drink roll is an exact, independently sourced six-sided result—face 1 means unicorn—and no probability is nudged.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>The 1-in-6 Frappuccino Promotion</title>
<style>
:root {
color-scheme: dark;
--bg: #080a12;
--panel: rgba(20, 23, 37, 0.86);
--panel-2: rgba(28, 32, 49, 0.9);
--line: rgba(255,255,255,0.1);
--text: #f7f5ef;
--muted: #a9adbd;
--green: #35c987;
--pink: #ff4fc8;
--cyan: #62e9ff;
--gold: #ffd166;
--danger: #ff496c;
}
* { box-sizing: border-box; }
html, body { min-height: 100%; }
body {
margin: 0;
background:
radial-gradient(circle at 15% 0%, rgba(32, 100, 80, 0.2), transparent 34rem),
radial-gradient(circle at 90% 18%, rgba(167, 36, 151, 0.16), transparent 30rem),
var(--bg);
color: var(--text);
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
overflow-x: hidden;
}
body::before {
content: "";
position: fixed;
inset: 0;
pointer-events: none;
background-image:
linear-gradient(rgba(255,255,255,0.018) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,0.018) 1px, transparent 1px);
background-size: 38px 38px;
mask-image: linear-gradient(to bottom, black, transparent 75%);
}
button, select { font: inherit; }
button:focus-visible, select:focus-visible {
outline: 2px solid var(--cyan);
outline-offset: 2px;
}
.app {
width: min(1480px, calc(100% - 28px));
margin: 0 auto;
padding: 18px 0 30px;
position: relative;
}
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 18px;
margin-bottom: 14px;
}
.brand {
display: flex;
align-items: center;
gap: 13px;
min-width: 0;
}
.siren {
width: 57px;
height: 57px;
flex: 0 0 57px;
border-radius: 50%;
display: grid;
place-items: center;
color: white;
background:
radial-gradient(circle, #139568 0 48%, transparent 49%),
conic-gradient(from -20deg, #35c987, #0a6b4c, #35c987, #0a6b4c, #35c987);
border: 3px solid #9cf1c8;
box-shadow: 0 0 28px rgba(53, 201, 135, 0.35);
font-size: 27px;
font-weight: 950;
}
.eyebrow {
color: var(--green);
font-size: 11px;
font-weight: 850;
letter-spacing: 0.18em;
text-transform: uppercase;
}
h1 {
margin: 2px 0 0;
font-size: clamp(21px, 3vw, 35px);
line-height: 1;
letter-spacing: -0.035em;
}
.header-actions {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
gap: 8px;
}
.pill-button, select {
color: var(--text);
background: rgba(255,255,255,0.065);
border: 1px solid var(--line);
border-radius: 999px;
padding: 9px 13px;
cursor: pointer;
transition: border-color 150ms, background 150ms, transform 150ms;
}
.pill-button:hover, select:hover {
border-color: rgba(255,255,255,0.27);
background: rgba(255,255,255,0.11);
}
.pill-button:active { transform: translateY(1px); }
.promo {
display: flex;
align-items: center;
justify-content: space-between;
gap: 15px;
padding: 12px 16px;
margin-bottom: 13px;
border: 1px solid rgba(255,79,200,0.27);
border-radius: 14px;
background:
linear-gradient(90deg, rgba(255,79,200,0.12), rgba(98,233,255,0.06) 48%, rgba(53,201,135,0.09)),
rgba(15,17,28,0.8);
overflow: hidden;
}
.promo strong {
color: #fff;
letter-spacing: 0.025em;
}
.promo span {
color: var(--muted);
font-size: 12px;
text-align: right;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(6, minmax(0, 1fr));
gap: 10px;
margin-bottom: 13px;
}
.stat {
min-width: 0;
padding: 13px 14px 12px;
border: 1px solid var(--line);
border-radius: 14px;
background: linear-gradient(145deg, rgba(255,255,255,0.06), rgba(255,255,255,0.025));
box-shadow: 0 10px 35px rgba(0,0,0,0.13);
}
.stat-label {
color: var(--muted);
font-size: 10px;
font-weight: 800;
letter-spacing: 0.1em;
text-transform: uppercase;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.stat-value {
margin-top: 5px;
font-size: clamp(20px, 2.25vw, 31px);
font-weight: 900;
letter-spacing: -0.04em;
font-variant-numeric: tabular-nums;
}
.stat-sub {
margin-top: 3px;
min-height: 15px;
color: var(--muted);
font-size: 11px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.stat:nth-child(2) .stat-value { color: var(--pink); }
.stat:nth-child(4) .stat-value { color: var(--danger); }
.stat:nth-child(5) .stat-value { color: var(--cyan); }
.stage-shell {
position: relative;
border: 1px solid rgba(255,255,255,0.13);
border-radius: 19px;
overflow: hidden;
background: #090b15;
box-shadow: 0 25px 90px rgba(0,0,0,0.44);
}
.stage {
position: relative;
aspect-ratio: 5 / 3;
min-height: 300px;
}
canvas {
display: block;
width: 100%;
height: 100%;
}
.stage-hud {
position: absolute;
inset: 14px 14px auto;
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 10px;
pointer-events: none;
}
.status-chip, .inside-chip {
display: flex;
align-items: center;
gap: 8px;
border: 1px solid rgba(255,255,255,0.12);
border-radius: 999px;
padding: 8px 11px;
background: rgba(8,10,18,0.78);
backdrop-filter: blur(8px);
box-shadow: 0 8px 25px rgba(0,0,0,0.22);
font-size: 11px;
font-weight: 850;
letter-spacing: 0.035em;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--green);
box-shadow: 0 0 12px var(--green);
}
.status-dot.warn {
background: var(--gold);
box-shadow: 0 0 12px var(--gold);
}
.status-dot.bad {
background: var(--danger);
box-shadow: 0 0 15px var(--danger);
}
.stage-note {
position: absolute;
left: 14px;
bottom: 12px;
right: 14px;
display: flex;
justify-content: space-between;
gap: 12px;
color: rgba(255,255,255,0.63);
font-size: 10px;
pointer-events: none;
text-shadow: 0 2px 8px #000;
}
.lower-grid {
display: grid;
grid-template-columns: 1.05fr 1.3fr 1fr;
gap: 11px;
margin-top: 13px;
}
.card {
border: 1px solid var(--line);
border-radius: 15px;
background: var(--panel);
padding: 14px;
min-width: 0;
}
.card-title {
margin: 0 0 10px;
color: var(--muted);
font-size: 10px;
font-weight: 850;
letter-spacing: 0.13em;
text-transform: uppercase;
}
.control-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
}
.control-row label {
color: var(--muted);
font-size: 12px;
margin-right: 2px;
}
select {
border-radius: 10px;
padding: 8px 28px 8px 10px;
}
.pressure-track {
height: 8px;
margin-top: 13px;
border-radius: 999px;
overflow: hidden;
background: rgba(255,255,255,0.08);
}
.pressure-fill {
height: 100%;
width: 0;
border-radius: inherit;
background: linear-gradient(90deg, var(--green), var(--gold), var(--pink), var(--danger));
box-shadow: 0 0 15px currentColor;
transition: width 180ms ease;
}
.pressure-copy {
display: flex;
justify-content: space-between;
gap: 10px;
margin-top: 7px;
color: var(--muted);
font-size: 11px;
}
.ledger {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 7px;
}
.die-column {
min-width: 0;
text-align: center;
color: var(--muted);
font-size: 10px;
}
.die-face {
font-size: 25px;
line-height: 1.1;
color: var(--text);
}
.die-column.unicorn .die-face {
color: var(--pink);
text-shadow: 0 0 13px var(--pink);
}
.die-bar {
height: 5px;
margin: 5px 2px;
border-radius: 999px;
background: rgba(255,255,255,0.08);
overflow: hidden;
}
.die-bar i {
display: block;
height: 100%;
min-width: 1px;
border-radius: inherit;
background: var(--cyan);
}
.die-column.unicorn .die-bar i { background: var(--pink); }
.die-count {
color: var(--text);
font-weight: 850;
font-variant-numeric: tabular-nums;
}
.diagnostics {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 9px;
}
.diagnostic {
min-width: 0;
padding: 9px 10px;
border: 1px solid rgba(255,255,255,0.08);
border-radius: 10px;
background: rgba(255,255,255,0.035);
}
.diagnostic span {
display: block;
color: var(--muted);
font-size: 9px;
font-weight: 800;
letter-spacing: 0.08em;
text-transform: uppercase;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.diagnostic b {
display: block;
margin-top: 4px;
font-size: 17px;
font-variant-numeric: tabular-nums;
}
.latest-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-top: 11px;
padding-top: 10px;
border-top: 1px solid var(--line);
}
.latest-die {
color: var(--pink);
font-size: 30px;
line-height: 1;
text-shadow: 0 0 14px rgba(255,79,200,0.7);
}
.truth {
text-align: right;
color: var(--muted);
font-size: 11px;
line-height: 1.4;
}
.truth strong { color: var(--text); }
.footer {
margin-top: 12px;
color: #777d91;
font-size: 11px;
text-align: center;
line-height: 1.55;
}
.footer strong { color: #aeb4c7; }
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0,0,0,0);
white-space: nowrap;
border: 0;
}
body.is-catastrophic .stage-shell {
border-color: rgba(255,73,108,0.7);
box-shadow: 0 0 50px rgba(255,73,108,0.28), 0 25px 90px rgba(0,0,0,0.44);
}
@media (max-width: 1050px) {
.stats-grid { grid-template-columns: repeat(3, 1fr); }
.lower-grid { grid-template-columns: 1fr 1fr; }
.control-card { grid-column: 1 / -1; }
}
@media (max-width: 680px) {
.app { width: min(100% - 14px, 1480px); padding-top: 10px; }
.topbar { align-items: flex-start; }
.siren { width: 44px; height: 44px; flex-basis: 44px; font-size: 21px; }
.header-actions { max-width: 48%; }
.pill-button { font-size: 11px; padding: 7px 9px; }
.promo { align-items: flex-start; flex-direction: column; }
.promo span { text-align: left; }
.stats-grid { grid-template-columns: repeat(2, 1fr); }
.lower-grid { grid-template-columns: 1fr; }
.control-card { grid-column: auto; }
.stage { min-height: 230px; }
.status-chip { max-width: 74%; }
.status-chip span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stage-note span:last-child { display: none; }
}
</style>
</head>
<body>
<main class="app">
<header class="topbar">
<div class="brand">
<div class="siren" aria-hidden="true">★</div>
<div>
<div class="eyebrow">Live promotional incident simulator</div>
<h1>THE 1-IN-6 FRAPPUCCINO</h1>
</div>
</div>
<div class="header-actions">
<button class="pill-button" id="soundBtn" type="button" aria-pressed="false">Sound: off</button>
<button class="pill-button" id="resetBtn" type="button">Reset counters</button>
</div>
</header>
<div class="promo">
<strong>BUY A FRAPPUCCINO. GET A 1/6 CHANCE OF A REALITY BUG.</strong>
<span>Three simultaneous unicorns voids the building, the lease, and possibly you.</span>
</div>
<section class="stats-grid" aria-label="Live statistics">
<article class="stat">
<div class="stat-label">Customers served</div>
<div class="stat-value" id="servedValue">0</div>
<div class="stat-sub" id="servedSub">Waiting for the first order</div>
</article>
<article class="stat">
<div class="stat-label">Unicorns produced</div>
<div class="stat-value" id="unicornValue">0</div>
<div class="stat-sub" id="unicornSub">0 ordinary drinks</div>
</article>
<article class="stat">
<div class="stat-label">Observed unicorn rate</div>
<div class="stat-value" id="rateValue">—</div>
<div class="stat-sub" id="rateSub">True chance: 16.67% · 1 in 6</div>
</article>
<article class="stat">
<div class="stat-label">Catastrophes</div>
<div class="stat-value" id="catValue">0</div>
<div class="stat-sub" id="catSub">No spatial violations yet</div>
</article>
<article class="stat">
<div class="stat-label">Inside now</div>
<div class="stat-value" id="insideValue">0 / 8</div>
<div class="stat-sub" id="insideSub">0 unicorn holders</div>
</article>
<article class="stat">
<div class="stat-label">Waiting outside</div>
<div class="stat-value" id="queueValue">0</div>
<div class="stat-sub" id="queueSub">The line remains orderly-ish</div>
</article>
</section>
<section class="stage-shell" aria-label="Live coffee shop simulation">
<div class="stage">
<canvas id="scene" aria-label="Animated cutaway coffee shop with customers waiting, ordering, drinking, and causing a neon catastrophe"></canvas>
<div class="stage-hud">
<div class="status-chip">
<i class="status-dot" id="statusDot"></i>
<span id="statusText">NORMAL COFFEE-ISH REALITY</span>
</div>
<div class="inside-chip" id="insideBadge">INSIDE 0/8 · UNICORNS 0</div>
</div>
<div class="stage-note">
<span>One independent six-sided roll per drink. Face 1 is the promotional defect.</span>
<span>Maximum 8 customers inside</span>
</div>
</div>
</section>
<section class="lower-grid">
<article class="card control-card">
<h2 class="card-title">Simulation controls</h2>
<div class="control-row">
<label for="speedSelect">Speed</label>
<select id="speedSelect" aria-label="Simulation speed">
<option value="0.5">0.5×</option>
<option value="1" selected>1×</option>
<option value="2">2×</option>
<option value="4">4×</option>
<option value="8">8×</option>
<option value="16">16×</option>
</select>
<button class="pill-button" id="pauseBtn" type="button" aria-pressed="false">Pause</button>
</div>
<div class="pressure-track" aria-hidden="true"><div class="pressure-fill" id="pressureFill"></div></div>
<div class="pressure-copy">
<span>Simultaneous unicorn coincidence pressure</span>
<strong id="pressureText">0 / 3</strong>
</div>
</article>
<article class="card">
<h2 class="card-title">Six-sided roll ledger</h2>
<div class="ledger" id="ledger"></div>
<div class="latest-row">
<div>
<div class="card-title" style="margin-bottom:2px">Latest drink roll</div>
<div class="latest-die" id="latestDie">—</div>
</div>
<div class="truth">
<strong>Face 1 = Neon Unicorn</strong><br>
Faces 2–6 = plausible coffee
</div>
</div>
</article>
<article class="card">
<h2 class="card-title">Amusingly important metrics</h2>
<div class="diagnostics">
<div class="diagnostic"><span>Drinks per event</span><b id="perEventValue">—</b></div>
<div class="diagnostic"><span>Since last event</span><b id="sinceEventValue">0</b></div>
<div class="diagnostic"><span>Voided by events</span><b id="voidedValue">0</b></div>
<div class="diagnostic"><span>Escaped with coffee</span><b id="escapedValue">0</b></div>
<div class="diagnostic"><span>Simulation clock</span><b id="clockValue">00:00</b></div>
<div class="diagnostic"><span>Simultaneous holders</span><b id="holdersValue">0</b></div>
</div>
</article>
</section>
<footer class="footer">
<strong>Promotion terms:</strong> no scheduled disasters, no pity rolls, no manually triggerable event.
The catastrophe condition is exactly three customers inside while holding unicorn drinks.
Cosmetic randomness is separate from the cryptographic drink roll.
</footer>
</main>
<div class="sr-only" id="announcer" aria-live="assertive"></div>
<script>
(() => {
"use strict";
const $ = id => document.getElementById(id);
const canvas = $("scene");
const ctx = canvas.getContext("2d");
const W = 1200;
const H = 720;
const SERVICE_ORDER = 1.6;
const SERVICE_BREW = 1.2;
const HOLD_TIME = 8.0;
const ENTER_SPEED = 360;
const EXIT_SPEED = 360;
const MAX_INSIDE = 8;
const SHOP = { x: 300, y: 90, w: 860, h: 530 };
const DOOR = { x: 300, y: 520 };
const OUT_TARGET = { x: 225, y: 640 };
const ORDER_SPOT = { x: 748, y: 380 };
const HOLD_SLOTS = [
{ x: 420, y: 500 }, { x: 505, y: 545 },
{ x: 590, y: 495 }, { x: 675, y: 540 },
{ x: 425, y: 585 }, { x: 510, y: 585 },
{ x: 595, y: 585 }, { x: 680, y: 585 }
];
const COLORS = ["#ff6b6b", "#4dabf7", "#ffd43b", "#51cf66", "#cc5de8", "#ff922b", "#22b8cf", "#f06595"];
const SKINS = ["#f2c6a0", "#c98c63", "#8f563d", "#ffd8b5", "#b97855"];
const DIE_GLYPHS = ["⚀", "⚁", "⚂", "⚃", "⚄", "⚅"];
const reducedMotion = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
let customers = [];
let waiting = [];
let counterQueue = [];
let orderActive = null;
let insideCount = 0;
let nextId = 1;
let nextHoldSlot = 0;
let spawnTimer = 0.5;
let speed = 1;
let paused = false;
let simTime = 0;
let visualTime = 0;
let event = null;
let particles = [];
let lastStatusText = "";
let lastFrame = performance.now();
let uiClock = 0;
const holders = new Set();
let stats = {
served: 0,
unicorns: 0,
cats: 0,
voided: 0,
escaped: 0,
lastRoll: 0,
lastCatServed: 0,
rollCounts: [0, 0, 0, 0, 0, 0]
};
const stars = Array.from({ length: 95 }, () => ({
x: Math.random() * W,
y: Math.random() * 470,
r: 0.5 + Math.random() * 1.7,
p: Math.random() * Math.PI * 2
}));
function rand(min, max) {
return min + Math.random() * (max - min);
}
function clamp(value, min, max) {
return Math.max(min, Math.min(max, value));
}
function distance(ax, ay, bx, by) {
return Math.hypot(bx - ax, by - ay);
}
function queuePosition(index) {
if (index < 32) {
const col = Math.floor(index / 8);
const row = index % 8;
return { x: 245 - col * 58, y: 635 - row * 59 };
}
return { x: 40, y: 640 - (index - 32) * 16 };
}
function spawnCustomer(initial = false) {
const id = nextId++;
const p = queuePosition(waiting.length);
const c = {
id,
phase: "WAIT",
x: initial ? p.x : rand(25, 90),
y: initial ? p.y : 682,
color: COLORS[(id - 1) % COLORS.length],
skin: SKINS[(id * 3) % SKINS.length],
size: rand(9.5, 12.5),
timer: 0,
path: [],
inside: false,
hasDrink: false,
unicorn: false,
holdSlot: 0,
remove: false,
hiddenDuringEvent: false
};
customers.push(c);
waiting.push(c);
return c;
}
function moveToward(c, target, velocity, dt) {
const dx = target.x - c.x;
const dy = target.y - c.y;
const d = Math.hypot(dx, dy);
const step = velocity * dt;
if (d <= step || d < 0.001) {
c.x = target.x;
c.y = target.y;
return true;
}
c.x += dx / d * step;
c.y += dy / d * step;
return false;
}
function advancePath(c, velocity, dt) {
while (c.path.length) {
if (!moveToward(c, c.path[0], velocity, dt)) return false;
c.path.shift();
}
return true;
}
function updateWaiting(dt) {
for (let i = 0; i < waiting.length; i++) {
const c = waiting[i];
if (c.hiddenDuringEvent) continue;
const p = queuePosition(i);
moveToward(c, p, 95, dt);
}
}
function admitNext() {
if (event || insideCount >= MAX_INSIDE) return;
if (counterQueue.some(c => c.phase === "ENTER")) return;
if (!waiting.length) return;
const c = waiting.shift();
const slot = counterQueue.length;
const spot = {
x: ORDER_SPOT.x + (slot % 2) * 43,
y: ORDER_SPOT.y + Math.floor(slot / 2) * 24
};
c.phase = "ENTER";
c.inside = true;
c.path = [
{ x: DOOR.x + 8, y: DOOR.y },
{ x: 425, y: 430 },
spot
];
insideCount++;
counterQueue.push(c);
}
function activateCounter() {
if (orderActive) return;
const c = counterQueue.find(customer => customer.phase === "COUNTER_WAIT");
if (!c) return;
orderActive = c;
c.phase = "ORDER";
c.timer = SERVICE_ORDER;
}
function allocateHoldSlot(c) {
const used = new Set(
customers
.filter(other => other !== c && other.phase === "HOLD")
.map(other => other.holdSlot)
);
for (let i = 0; i < HOLD_SLOTS.length; i++) {
if (!used.has(i)) return i;
}
const fallback = nextHoldSlot++ % HOLD_SLOTS.length;
return fallback;
}
// Exact uniform six-sided result. Rejecting 252–255 leaves 252 accepted
// byte values, which divides evenly by six. Cosmetic RNG never touches this.
function rollSix() {
if (window.crypto && window.crypto.getRandomValues) {
const byte = new Uint8Array(1);
do {
window.crypto.getRandomValues(byte);
} while (byte[0] >= 252);
return (byte[0] % 6) + 1;
}
return 1 + Math.floor(Math.random() * 6);
}
function receiveDrink(c) {
const face = rollSix();
const isUnicorn = face === 1;
stats.lastRoll = face;
stats.rollCounts[face - 1]++;
stats.served++;
c.hasDrink = true;
c.unicorn = isUnicorn;
c.phase = "HOLD";
c.timer = HOLD_TIME;
c.holdSlot = allocateHoldSlot(c);
c.path = [];
if (isUnicorn) {
stats.unicorns++;
holders.add(c.id);
playUnicornSound();
} else {
playRegisterSound();
}
const index = counterQueue.indexOf(c);
if (index >= 0) counterQueue.splice(index, 1);
orderActive = null;
activateCounter();
admitNext();
// This is the only catastrophe trigger. It is a state test, not a timer,
// quota, counter of total unicorns, or probability adjustment.
if (holders.size >= 3) {
triggerCatastrophe(c);
}
}
function crossExit(c) {
if (c.inside) {
c.inside = false;
insideCount = Math.max(0, insideCount - 1);
}
if (c.unicorn) holders.delete(c.id);
stats.escaped++;
c.phase = "OUT";
c.path = [];
c.x = DOOR.x - 7;
c.y = DOOR.y;
admitNext();
}
function updateCustomers(dt) {
for (const c of customers.slice()) {
if (event) break;
if (c.phase === "ENTER") {
if (advancePath(c, ENTER_SPEED, dt)) {
c.phase = "COUNTER_WAIT";
c.path = [];
activateCounter();
admitNext();
}
} else if (c.phase === "COUNTER_WAIT") {
if (!orderActive) activateCounter();
} else if (c.phase === "ORDER") {
c.timer -= dt;
if (c.timer <= 0) {
c.phase = "BREW";
c.timer += SERVICE_BREW;
}
} else if (c.phase === "BREW") {
c.timer -= dt;
if (c.timer <= 0) receiveDrink(c);
} else if (c.phase === "HOLD") {
c.timer -= dt;
moveToward(c, HOLD_SLOTS[c.holdSlot], 230, dt);
if (c.timer <= 0) {
c.phase = "EXIT";
c.path = [{ x: DOOR.x - 5, y: DOOR.y }];
}
} else if (c.phase === "EXIT") {
if (advancePath(c, EXIT_SPEED, dt)) crossExit(c);
} else if (c.phase === "OUT") {
if (moveToward(c, OUT_TARGET, 250, dt)) c.remove = true;
}
}
customers = customers.filter(c => !c.remove);
}
function update(dt) {
simTime += dt;
spawnTimer -= dt;
if (spawnTimer <= 0) {
spawnCustomer(false);
spawnTimer = rand(2.0, 2.7);
}
updateWaiting(dt);
activateCounter();
admitNext();
updateCustomers(dt);
updateParticles(dt);
const held = holders.size;
if (held > 0 && Math.random() < dt * (held === 2 ? 8 : 2.5)) {
const owner = customers.find(c => holders.has(c.id));
if (owner) {
particles.push({
x: owner.x + rand(-14, 14),
y: owner.y + rand(-18, 12),
vx: rand(-18, 18),
vy: rand(-38, -12),
gravity: 0,
life: rand(0.8, 1.8),
maxLife: 1.8,
size: rand(1.5, 4),
color: held === 2 ? ["#ff4fc8", "#62e9ff", "#ffd166"][Math.floor(Math.random() * 3)] : "#ff4fc8",
kind: "mote",
rotation: 0,
spin: 0
});
}
}
}
function createExplosion(x, y) {
const colors = ["#ff4fc8", "#62e9ff", "#ffd166", "#7cf67c", "#ffffff", "#ff496c"];
for (let i = 0; i < 180; i++) {
const angle = rand(0, Math.PI * 2);
const speed = rand(70, 430);
const life = rand(1.3, 4.2);
particles.push({
x: x + rand(-40, 40),
y: y + rand(-35, 35),
vx: Math.cos(angle) * speed,
vy: Math.sin(angle) * speed - rand(20, 140),
gravity: rand(70, 150),
life,
maxLife: life,
size: rand(2, 9),
color: colors[i % colors.length],
kind: i % 4 === 0 ? "debris" : "spark",
rotation: rand(0, Math.PI),
spin: rand(-7, 7)
});
}
}
function triggerCatastrophe(trigger) {
if (event) return;
const inside = customers.filter(c => c.inside);
const destroyedDrinks = inside.filter(c => c.hasDrink).length;
const ghosts = inside.map(c => ({
id: c.id,
x: c.x,
y: c.y,
color: c.color,
skin: c.skin,
size: c.size,
unicorn: c.unicorn,
hasDrink: c.hasDrink
}));
stats.cats++;
stats.voided += destroyedDrinks;
stats.lastCatServed = stats.served;
for (const c of inside) {
c.phase = "WAIT";
c.inside = false;
c.hasDrink = false;
c.unicorn = false;
c.path = [];
c.timer = 0;
c.hiddenDuringEvent = true;
}
waiting = [...inside, ...waiting];
counterQueue = [];
orderActive = null;
insideCount = 0;
holders.clear();
event = {
elapsed: 0,
duration: 7.2,
rebuildStart: 4.7,
ghosts,
triggerX: trigger.x,
triggerY: trigger.y
};
createExplosion(trigger.x, trigger.y);
playCatastropheSound();
announce("NEON UNICORN EVENT. Three unicorn holders were inside simultaneously. The building has become a rumor.");
document.body.classList.add("is-catastrophic");
}
function finishCatastrophe() {
event = null;
for (const c of customers) c.hiddenDuringEvent = false;
for (let i = 0; i < waiting.length; i++) {
const p = queuePosition(i);
waiting[i].x = p.x;
waiting[i].y = p.y;
}
counterQueue = [];
orderActive = null;
insideCount = 0;
holders.clear();
spawnTimer = 0.45;
document.body.classList.remove("is-catastrophic");
announce("The shop has rebuilt itself. The promotion remains unfortunately active.");
}
function updateParticles(dt) {
for (const p of particles) {
p.life -= dt;
p.x += p.vx * dt;
p.y += p.vy * dt;
p.vy += p.gravity * dt;
p.vx *= Math.pow(0.994, dt * 60);
p.rotation += p.spin * dt;
}
particles = particles.filter(p => p.life > 0);
}
function resetSimulation() {
event = null;
customers = [];
waiting = [];
counterQueue = [];
orderActive = null;
insideCount = 0;
nextId = 1;
nextHoldSlot = 0;
spawnTimer = 0.5;
simTime = 0;
visualTime = 0;
particles = [];
holders.clear();
lastStatusText = "";
stats = {
served: 0,
unicorns: 0,
cats: 0,
voided: 0,
escaped: 0,
lastRoll: 0,
lastCatServed: 0,
rollCounts: [0, 0, 0, 0, 0, 0]
};
for (let i = 0; i < 4; i++) spawnCustomer(true);
admitNext();
document.body.classList.remove("is-catastrophic");
announce("Simulation reset. Four customers are outside and the dice are innocent.");
updateUI();
}
function announce(message) {
$("announcer").textContent = message;
}
function formatTime(seconds) {
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return String(mins).padStart(2, "0") + ":" + String(secs).padStart(2, "0");
}
function updateUI() {
const held = holders.size;
const rate = stats.served ? stats.unicorns / stats.served * 100 : 0;
const difference = rate - 100 / 6;
const ordinary = stats.served - stats.unicorns;
$("servedValue").textContent = stats.served.toLocaleString();
$("servedSub").textContent = stats.served
? `${stats.escaped.toLocaleString()} orders escaped with coffee`
: "Waiting for the first order";
$("unicornValue").textContent = stats.unicorns.toLocaleString();
$("unicornSub").textContent = `${ordinary.toLocaleString()} ordinary drinks`;
$("rateValue").textContent = stats.served ? rate.toFixed(2) + "%" : "—";
$("rateSub").textContent = stats.served
? `${difference >= 0 ? "+" : ""}${difference.toFixed(2)} pp vs 16.67% true`
: "True chance: 16.67% · 1 in 6";
$("catValue").textContent = stats.cats.toLocaleString();
$("catSub").textContent = stats.cats
? `${(stats.served / stats.cats).toFixed(1)} drinks per event`
: "No spatial violations yet";
$("insideValue").textContent = `${insideCount} / ${MAX_INSIDE}`;
$("insideSub").textContent = `${held} unicorn holder${held === 1 ? "" : "s"}`;
$("queueValue").textContent = waiting.length.toLocaleString();
$("queueSub").textContent = waiting.length > 10 ? "The line is becoming a topology" : "The line remains orderly-ish";
$("insideBadge").textContent = `INSIDE ${insideCount}/${MAX_INSIDE} · UNICORNS ${held}`;
$("pressureFill").style.width = `${event ? 100 : clamp(held / 3, 0, 1) * 100}%`;
$("pressureText").textContent = event ? "EVENT / 3" : `${held} / 3`;
$("holdersValue").textContent = held;
let status = "NORMAL COFFEE-ISH REALITY";
let dotClass = "";
if (event) {
status = "NEON UNICORN EVENT — REALITY HAS LEFT THE BUILDING";
dotClass = "bad";
} else if (held === 2) {
status = "TWO UNICORNS — COINCIDENCE DEBT IS NOW";
dotClass = "bad";
} else if (held === 1) {
status = "ONE UNICORN — THE VENTS ARE LYING";
dotClass = "warn";
}
$("statusText").textContent = status;
$("statusDot").className = "status-dot " + dotClass;
$("perEventValue").textContent = stats.cats ? (stats.served / stats.cats).toFixed(1) : "—";
$("sinceEventValue").textContent = (stats.served - stats.lastCatServed).toLocaleString();
$("voidedValue").textContent = stats.voided.toLocaleString();
$("escapedValue").textContent = stats.escaped.toLocaleString();
$("clockValue").textContent = formatTime(simTime);
$("latestDie").textContent = stats.lastRoll ? `${DIE_GLYPHS[stats.lastRoll - 1]} ${stats.lastRoll}` : "—";
const maxCount = Math.max(1, ...stats.rollCounts);
$("ledger").innerHTML = stats.rollCounts.map((count, i) => `
<div class="die-column ${i === 0 ? "unicorn" : ""}">
<div class="die-face">${DIE_GLYPHS[i]}</div>
<div class="die-bar"><i style="width:${count / maxCount * 100}%"></i></div>
<div class="die-count">${count}</div>
</div>
`).join("");
}
function roundedRect(x, y, w, h, r) {
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.arcTo(x + w, y, x + w, y + h, r);
ctx.arcTo(x + w, y + h, x, y + h, r);
ctx.arcTo(x, y + h, x, y, r);
ctx.arcTo(x, y, x + w, y, r);
ctx.closePath();
}
function circle(x, y, radius, color) {
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fill();
}
function star(x, y, outer, inner, color) {
ctx.fillStyle = color;
ctx.beginPath();
for (let i = 0; i < 10; i++) {
const a = -Math.PI / 2 + i * Math.PI / 5;
const r = i % 2 === 0 ? outer : inner;
const px = x + Math.cos(a) * r;
const py = y + Math.sin(a) * r;
if (i === 0) ctx.moveTo(px, py);
else ctx.lineTo(px, py);
}
ctx.closePath();
ctx.fill();
}
function drawBackground(t) {
const gradient = ctx.createLinearGradient(0, 0, 0, H);
if (event) {
const hue = (t * 35) % 360;
gradient.addColorStop(0, `hsl(${hue}, 55%, 12%)`);
gradient.addColorStop(0.55, `hsl(${(hue + 90) % 360}, 48%, 18%)`);
gradient.addColorStop(1, "#090b15");
} else {
gradient.addColorStop(0, "#090b19");
gradient.addColorStop(0.58, "#11192a");
gradient.addColorStop(1, "#171526");
}
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, W, H);
ctx.save();
for (const s of stars) {
ctx.globalAlpha = 0.28 + (Math.sin(t * 1.5 + s.p) + 1) * 0.22;
circle(s.x, s.y, s.r, "#d9f6ff");
}
ctx.restore();
ctx.save();
ctx.shadowColor = "rgba(98,233,255,0.28)";
ctx.shadowBlur = 25;
circle(1060, 68, 29, "#dffaff");
ctx.restore();
ctx.fillStyle = "#222536";
ctx.fillRect(0, 608, W, 74);
ctx.fillStyle = "#34384a";
ctx.fillRect(0, 608, W, 8);
ctx.fillStyle = "rgba(255,255,255,0.06)";
for (let x = 25; x < W; x += 72) ctx.fillRect(x, 647, 38, 3);
ctx.fillStyle = "#111522";
ctx.fillRect(0, 682, W, 38);
ctx.fillStyle = "rgba(255,216,102,0.35)";
for (let x = -20; x < W; x += 120) ctx.fillRect(x, 703, 62, 4);
ctx.save();
ctx.strokeStyle = "rgba(255,255,255,0.08)";
ctx.lineWidth = 4;
ctx.beginPath();
ctx.moveTo(120, 608);
ctx.lineTo(120, 520);
ctx.quadraticCurveTo(120, 492, 148, 492);
ctx.stroke();
circle(148, 486, 12, event ? "#ff496c" : "#ffe2a8");
ctx.restore();
}
function drawLamp(x, y, t, held) {
ctx.save();
ctx.strokeStyle = "rgba(255,255,255,0.18)";
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(x, 155);
ctx.lineTo(x, y);
ctx.stroke();
let color = "#ffdca4";
if (held === 1) color = Math.sin(t * 5) > 0 ? "#ff8bdc" : "#ffdca4";
if (held === 2) color = Math.sin(t * 8) > 0.15 ? "#ff4fc8" : (Math.sin(t * 13) > 0 ? "#62e9ff" : "#ffe26b");
ctx.shadowColor = color;
ctx.shadowBlur = held === 2 ? 28 : 13;
circle(x, y + 4, 9, color);
ctx.fillStyle = "#151827";
ctx.fillRect(x - 12, y - 2, 24, 7);
ctx.restore();
}
function drawShop(t) {
const held = holders.size;
ctx.save();
ctx.shadowColor = "rgba(0,0,0,0.55)";
ctx.shadowBlur = 35;
ctx.shadowOffsetY = 18;
roundedRect(SHOP.x, SHOP.y, SHOP.w, SHOP.h, 20);
ctx.fillStyle = "#111824";
ctx.fill();
ctx.shadowColor = "transparent";
ctx.shadowBlur = 0;
ctx.shadowOffsetY = 0;
const wall = held === 2
? `hsl(${(t * 90) % 360}, 30%, 17%)`
: held === 1
? "#1b2830"
: "#172a27";
ctx.fillStyle = wall;
roundedRect(SHOP.x + 8, SHOP.y + 8, SHOP.w - 16, SHOP.h - 16, 15);
ctx.fill();
ctx.fillStyle = "#28243b";
ctx.fillRect(SHOP.x + 8, 382, SHOP.w - 16, 230);
ctx.fillStyle = "rgba(255,255,255,0.035)";
for (let x = 320; x < 1160; x += 85) ctx.fillRect(x, 388, 2, 220);
ctx.fillStyle = "rgba(98,233,255,0.045)";
ctx.fillRect(1000, 180, 130, 145);
ctx.strokeStyle = "rgba(98,233,255,0.22)";
ctx.lineWidth = 2;
ctx.strokeRect(1000, 180, 130, 145);
ctx.beginPath();
ctx.moveTo(1065, 180);
ctx.lineTo(1065, 325);
ctx.moveTo(1000, 252);
ctx.lineTo(1130, 252);
ctx.stroke();
ctx.fillStyle = "rgba(255,255,255,0.06)";
roundedRect(462, 205, 196, 118, 9);
ctx.fill();
ctx.fillStyle = "#ff6ec7";
ctx.fillRect(478, 221, 162, 35);
ctx.fillStyle = "#151621";
ctx.font = "900 18px system-ui";
ctx.textAlign = "center";
ctx.fillText("1 / 6 PROMO", 559, 246);
ctx.fillStyle = "#c9d2d7";
ctx.font = "700 12px system-ui";
ctx.fillText("PROMOTIONAL REALITY BUG", 559, 278);
ctx.fillText("LIMIT: THREE PER ROOM", 559, 298);
ctx.save();
ctx.shadowColor = held ? "rgba(255,79,200,0.6)" : "rgba(53,201,135,0.45)";
ctx.shadowBlur = held ? 30 : 18;
ctx.fillStyle = "#07875d";
roundedRect(375, 112, 265, 60, 9);
ctx.fill();
ctx.restore();
ctx.fillStyle = "#fff";
ctx.font = "900 30px system-ui";
ctx.textAlign = "center";
ctx.fillText("STARBUCKS", 507, 150);
ctx.font = "800 9px system-ui";
ctx.fillText("UNSANCTIONED COFFEE & FINE VIOLATIONS", 507, 165);
ctx.save();
ctx.shadowColor = "rgba(255,79,200,0.55)";
ctx.shadowBlur = 20;
ctx.fillStyle = "#d728a7";
roundedRect(670, 114, 445, 55, 9);
ctx.fill();
ctx.restore();
ctx.fillStyle = "#fff";
ctx.font = "900 23px system-ui";
ctx.fillText("NEON UNICORN FRAPPUCCINO", 892, 140);
ctx.font = "800 10px system-ui";
ctx.fillText("1 IN 6 DRINKS VIOLATES THE CONCEPT OF COFFEE", 892, 157);
ctx.fillStyle = "rgba(255,209,102,0.13)";
roundedRect(986, 350, 142, 145, 8);
ctx.fill();
ctx.strokeStyle = "rgba(255,209,102,0.5)";
ctx.lineWidth = 2;
ctx.stroke();
ctx.fillStyle = "#ffd166";
ctx.font = "900 15px system-ui";
ctx.fillText("PROMOTION", 1057, 378);
ctx.font = "800 10px system-ui";
ctx.fillText("ACTUAL FINE PRINT", 1057, 397);
ctx.fillText("1. MIX FRAPPUCCINO", 1057, 426);
ctx.fillText("2. ROLL ONE DIE", 1057, 446);
ctx.fillText("3. HANDLE RESULT", 1057, 466);
ctx.fillText("4. REGRET NOTHING", 1057, 486);
ctx.fillStyle = "#31283b";
roundedRect(710, 245, 240, 92, 10);
ctx.fill();
ctx.fillStyle = "#c49b72";
roundedRect(702, 235, 256, 25, 7);
ctx.fill();
ctx.fillStyle = "#11131d";
roundedRect(733, 262, 92, 40, 7);
ctx.fill();
ctx.fillStyle = "#68e2d4";
ctx.font = "800 10px system-ui";
ctx.fillText("ORDER →", 779, 287);
ctx.fillStyle = "#162a24";
roundedRect(947, 216, 178, 20, 5);
ctx.fill();
for (let i = 0; i < 6; i++) {
const cupColor = i === 2 ? "#ff64cf" : "#f2d5b5";
ctx.fillStyle = cupColor;
ctx.fillRect(960 + i * 26, 190, 15, 26);
ctx.fillStyle = "#ffffff";
ctx.fillRect(963 + i * 26, 186, 9, 5);
}
ctx.save();
ctx.translate(838, 267);
ctx.fillStyle = "#8f563d";
circle(0, -9, 11, ctx.fillStyle);
ctx.fillStyle = "#f2c6a0";
circle(0, -11, 8, ctx.fillStyle);
ctx.fillStyle = "#07875d";
roundedRect(-15, 0, 30, 39, 8);
ctx.fill();
ctx.fillStyle = "#d9fff0";
ctx.font = "900 9px system-ui";
ctx.textAlign = "center";
ctx.fillText("BARISTA", 0, 23);
ctx.restore();
const stoolXs = [425, 635];
for (const x of stoolXs) {
ctx.fillStyle = "#4a4155";
ctx.fillRect(x - 4, 430, 8, 48);
ctx.fillStyle = "#675a72";
circle(x, 481, 19, "#675a72");
}
ctx.fillStyle = "#172d27";
ctx.fillRect(1090, 510, 42, 57);
for (let i = 0; i < 6; i++) {
ctx.save();
ctx.translate(1111, 510);
ctx.rotate(i * Math.PI / 3);
ctx.fillStyle = i % 2 ? "#35c987" : "#138b60";
ctx.beginPath();
ctx.ellipse(0, -25, 10, 30, 0, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
ctx.strokeStyle = "#4e5966";
ctx.lineWidth = 7;
ctx.beginPath();
ctx.moveTo(SHOP.x, 425);
ctx.lineTo(SHOP.x, 590);
ctx.moveTo(SHOP.x + 30, 425);
ctx.lineTo(SHOP.x + 30, 590);
ctx.stroke();
ctx.fillStyle = "rgba(98,233,255,0.08)";
ctx.fillRect(SHOP.x + 4, 431, 23, 151);
ctx.fillStyle = "#7ce7d5";
circle(SHOP.x + 21, 507, 2.5, "#7ce7d5");
drawLamp(545, 205, t, held);
drawLamp(680, 205, t, held);
drawLamp(1020, 205, t, held);
if (held === 1) {
ctx.save();
ctx.globalCompositeOperation = "screen";
const glow = ctx.createRadialGradient(520, 450, 10, 520, 450, 210);
glow.addColorStop(0, "rgba(255,79,200,0.08)");
glow.addColorStop(1, "rgba(255,79,200,0)");
ctx.fillStyle = glow;
ctx.fillRect(SHOP.x, SHOP.y, SHOP.w, SHOP.h);
ctx.restore();
}
ctx.restore();
}
function drawRuins(t) {
ctx.save();
ctx.fillStyle = "rgba(7,8,14,0.72)";
ctx.fillRect(SHOP.x, SHOP.y, SHOP.w, SHOP.h);
ctx.fillStyle = "#242135";
ctx.beginPath();
ctx.moveTo(SHOP.x + 10, SHOP.y + 15);
ctx.lineTo(585, SHOP.y + 8);
ctx.lineTo(655, 330);
ctx.lineTo(560, 405);
ctx.lineTo(SHOP.x + 5, 370);
ctx.closePath();
ctx.fill();
ctx.fillStyle = "#30243c";
ctx.beginPath();
ctx.moveTo(955, SHOP.y + 10);
ctx.lineTo(SHOP.x + SHOP.w - 8, SHOP.y + 25);
ctx.lineTo(SHOP.x + SHOP.w - 15, 385);
ctx.lineTo(1000, 335);
ctx.closePath();
ctx.fill();
ctx.strokeStyle = "rgba(255,79,200,0.5)";
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(505, 90);
ctx.lineTo(548, 170);
ctx.lineTo(520, 232);
ctx.lineTo(570, 320);
ctx.moveTo(980, 95);
ctx.lineTo(930, 170);
ctx.lineTo(960, 245);
ctx.stroke();
ctx.save();
ctx.translate(470, 465);
ctx.rotate(-0.23);
ctx.fillStyle = "#514256";
ctx.fillRect(-125, -30, 250, 60);
ctx.fillStyle = "#a27a5c";
ctx.fillRect(-135, -44, 270, 18);
ctx.restore();
ctx.save();
ctx.translate(740, 143);
ctx.rotate(0.28);
ctx.fillStyle = "#087a58";
ctx.fillRect(-125, -22, 250, 48);
ctx.fillStyle = "#fff";
ctx.font = "900 26px system-ui";
ctx.textAlign = "center";
ctx.fillText("STARBUCKS", 0, 9);
ctx.restore();
ctx.strokeStyle = "rgba(255,255,255,0.17)";
ctx.lineWidth = 2;
for (let i = 0; i < 9; i++) {
const x = 330 + i * 87;
ctx.beginPath();
ctx.moveTo(x, 610);
ctx.lineTo(x + rand(-30, 30), 560);
ctx.lineTo(x + rand(-45, 45), 520);
ctx.stroke();
}
ctx.save();
ctx.globalCompositeOperation = "screen";
const puddle = ctx.createRadialGradient(720, 540, 10, 720, 540, 250);
puddle.addColorStop(0, "rgba(255,79,200,0.3)");
puddle.addColorStop(0.55, "rgba(98,233,255,0.1)");
puddle.addColorStop(1, "rgba(0,0,0,0)");
ctx.fillStyle = puddle;
ctx.fillRect(430, 390, 650, 230);
ctx.restore();
ctx.restore();
}
function drawRebuild(t) {
if (!event) return;
const progress = clamp((event.elapsed - event.rebuildStart) / (event.duration - event.rebuildStart), 0, 1);
if (progress <= 0) return;
ctx.save();
ctx.beginPath();
ctx.rect(0, H - progress * H, W, progress * H);
ctx.clip();
ctx.globalAlpha = Math.min(1, progress * 1.4);
drawShop(t);
ctx.restore();
}
function drawCustomer(c, ghost = false) {
ctx.save();
if (ghost) ctx.globalAlpha = Math.max(0, 1 - (event ? event.elapsed / 4.2 : 0));
ctx.translate(c.x, c.y);
if (c.phase === "WAIT") ctx.scale(0.9, 0.9);
if (c.unicorn) {
const pulse = 1 + Math.sin(visualTime * 7) * 0.12;
const glow = ctx.createRadialGradient(0, 0, 3, 0, 0, 38 * pulse);
glow.addColorStop(0, "rgba(255,255,255,0.44)");
glow.addColorStop(0.2, "rgba(255,79,200,0.32)");
glow.addColorStop(0.58, "rgba(98,233,255,0.17)");
glow.addColorStop(1, "rgba(255,79,200,0)");
ctx.fillStyle = glow;
ctx.beginPath();
ctx.arc(0, 0, 38 * pulse, 0, Math.PI * 2);
ctx.fill();
}
ctx.fillStyle = "rgba(0,0,0,0.25)";
ctx.beginPath();
ctx.ellipse(0, 17, c.size * 1.1, 4, 0, 0, Math.PI * 2);
ctx.fill();
circle(0, -11, c.size * 0.67, c.skin);
circle(0, 3, c.size, c.color);
const faceX = c.phase === "EXIT" || c.phase === "OUT" ? -1.5 : 1.5;
circle(faceX, -12, 1.25, "#111522");
circle(faceX + 5, -12, 1.25, "#111522");
if (c.hasDrink || c.unicorn) {
ctx.save();
ctx.translate(c.size + 7, 1);
ctx.rotate(-0.12);
if (c.unicorn) {
const cup = ctx.createLinearGradient(-7, 0, 8, 0);
cup.addColorStop(0, "#62e9ff");
cup.addColorStop(0.5, "#ff4fc8");
cup.addColorStop(1, "#ffd166");
ctx.fillStyle = cup;
ctx.shadowColor = "#ff4fc8";
ctx.shadowBlur = 12;
} else {
ctx.fillStyle = "#d8b28b";
}
ctx.beginPath();
ctx.moveTo(-7, -7);
ctx.lineTo(7, -7);
ctx.lineTo(5, 10);
ctx.lineTo(-5, 10);
ctx.closePath();
ctx.fill();
ctx.fillStyle = "#fff4dc";
ctx.fillRect(-8, -9, 16, 4);
ctx.fillStyle = "#704628";
ctx.fillRect(-1, -13, 2, 6);
if (c.unicorn) {
ctx.fillStyle = "#fff";
ctx.beginPath();
ctx.moveTo(0, -18);
ctx.lineTo(-3, -11);
ctx.lineTo(3, -11);
ctx.closePath();
ctx.fill();
star(0, -27, 4, 1.6, "#fff36c");
}
ctx.restore();
}
if (c.unicorn) {
star(0, -c.size - 23, 4.2, 1.7, "#fff36c");
}
ctx.restore();
}
function drawGiantUnicorn() {
if (!event) return;
const e = event.elapsed;
if (e < 0.45) return;
const appear = clamp((e - 0.45) / 0.8, 0, 1);
const fade = e < 4.7 ? 1 : clamp(1 - (e - 4.7) / 2.4, 0, 1);
const dance = reducedMotion ? 0.2 : Math.sin(e * 7.4);
const bounce = reducedMotion ? 0 : Math.abs(Math.sin(e * 7.4)) * 18;
const hue = (e * 65) % 360;
ctx.save();
ctx.translate(725, 375 - bounce);
ctx.scale(appear, appear);
ctx.globalAlpha = fade;
ctx.save();
ctx.rotate(e * 0.34);
const rayColors = ["#ff4fc8", "#62e9ff", "#ffd166", "#7cf67c", "#b36cff"];
for (let i = 0; i < 18; i++) {
const a = i / 18 * Math.PI * 2;
ctx.fillStyle = rayColors[i % rayColors.length];
ctx.globalAlpha = fade * 0.085;
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.arc(0, 0, 620, a, a + 0.11);
ctx.closePath();
ctx.fill();
}
ctx.restore();
ctx.lineCap = "round";
const legs = [-72, -30, 40, 80];
for (let i = 0; i < legs.length; i++) {
const x = legs[i];
const swing = Math.sin(e * 7.4 + i * 1.7) * 25;
ctx.strokeStyle = i % 2 ? "#e7d6ff" : "#fff1ff";
ctx.lineWidth = 17;
ctx.beginPath();
ctx.moveTo(x, 20);
ctx.lineTo(x + swing, 78);
ctx.lineTo(x + swing * 0.4, 125);
ctx.stroke();
ctx.fillStyle = "#ff4fc8";
ctx.beginPath();
ctx.ellipse(x + swing * 0.4, 128, 17, 8, 0, 0, Math.PI * 2);
ctx.fill();
}
ctx.strokeStyle = "#ff4fc8";
ctx.lineWidth = 13;
ctx.beginPath();
ctx.moveTo(-90, -5);
ctx.bezierCurveTo(-175, -10, -160, -95, -112, -68);
ctx.stroke();
const body = ctx.createLinearGradient(-110, -80, 110, 80);
body.addColorStop(0, `hsl(${hue}, 85%, 67%)`);
body.addColorStop(0.45, "#f5eaff");
body.addColorStop(1, "#8f65ff");
ctx.fillStyle = body;
ctx.shadowColor = "#ff4fc8";
ctx.shadowBlur = 28;
ctx.beginPath();
ctx.ellipse(0, 0, 116, 64, -0.06, 0, Math.PI * 2);
ctx.fill();
ctx.shadowBlur = 0;
ctx.fillStyle = "#fff0ff";
ctx.beginPath();
ctx.moveTo(42, -31);
ctx.lineTo(83, -102);
ctx.lineTo(127, -24);
ctx.closePath();
ctx.fill();
const head = ctx.createLinearGradient(60, -150, 150, -50);
head.addColorStop(0, "#ffffff");
head.addColorStop(1, "#d7b9ff");
ctx.fillStyle = head;
ctx.beginPath();
ctx.ellipse(103, -93, 54, 43, -0.12, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = "#ff9adf";
ctx.beginPath();
ctx.moveTo(119, -78);
ctx.lineTo(166, -66);
ctx.lineTo(122, -52);
ctx.closePath();
ctx.fill();
const maneColors = ["#ff4fc8", "#ffd166", "#7cf67c", "#62e9ff", "#a878ff"];
for (let i = 0; i < 11; i++) {
const a = -2.75 + i * 0.27;
const x = 86 + Math.cos(a) * 69;
const y = -89 + Math.sin(a) * 67;
ctx.fillStyle = maneColors[i % maneColors.length];
ctx.beginPath();
ctx.moveTo(85, -90);
ctx.lineTo(x, y);
ctx.lineTo(x + 22, y + 15);
ctx.closePath();
ctx.fill();
}
ctx.fillStyle = "#fff";
ctx.shadowColor = "#62e9ff";
ctx.shadowBlur = 18;
ctx.beginPath();
ctx.moveTo(91, -124);
ctx.lineTo(108, -178);
ctx.lineTo(120, -120);
ctx.closePath();
ctx.fill();
ctx.shadowBlur = 0;
circle(116, -102, 8, "#111522");
circle(118, -104, 2.4, "#ffffff");
ctx.strokeStyle = "#111522";
ctx.lineWidth = 3;
ctx.beginPath();
ctx.arc(136, -78, 8, 0.15, Math.PI * 0.8);
ctx.stroke();
ctx.fillStyle = "#ff4fc8";
ctx.font = "900 15px system-ui";
ctx.textAlign = "center";
ctx.fillText("DANCE BREAK", 0, 171);
ctx.restore();
}
function drawParticles() {
for (const p of particles) {
ctx.save();
const alpha = clamp(p.life / p.maxLife, 0, 1);
ctx.globalAlpha = alpha;
if (p.kind === "debris") {
ctx.translate(p.x, p.y);
ctx.rotate(p.rotation);
ctx.fillStyle = p.color;
ctx.fillRect(-p.size / 2, -p.size / 3, p.size, p.size * 0.66);
} else {
ctx.shadowColor = p.color;
ctx.shadowBlur = p.kind === "mote" ? 8 : 13;
circle(p.x, p.y, p.size * (0.45 + alpha * 0.55), p.color);
}
ctx.restore();
}
}
function currentShake() {
if (reducedMotion) return 0;
if (event) {
if (event.elapsed < 0.8) return 20 * (1 - event.elapsed / 0.8) + 5;
if (event.elapsed < 4.7) return 4;
return 3 * clamp(1 - (event.elapsed - 4.7) / 2.5, 0, 1);
}
if (holders.size === 2) return 1.7;
return 0;
}
function drawEventOverlay() {
if (!event) return;
const e = event.elapsed;
if (e < 0.38 && !reducedMotion) {
ctx.fillStyle = `rgba(255,255,255,${(1 - e / 0.38) * 0.72})`;
ctx.fillRect(0, 0, W, H);
}
ctx.save();
const banner = ctx.createLinearGradient(390, 0, 810, 0);
banner.addColorStop(0, "rgba(255,73,108,0)");
banner.addColorStop(0.15, "rgba(255,73,108,0.9)");
banner.addColorStop(0.5, "rgba(153,55,255,0.95)");
banner.addColorStop(0.85, "rgba(255,73,108,0.9)");
banner.addColorStop(1, "rgba(255,73,108,0)");
ctx.fillStyle = banner;
ctx.fillRect(330, 25, 540, 84);
ctx.textAlign = "center";
ctx.fillStyle = "#fff";
ctx.font = "950 42px system-ui";
ctx.fillText("NEON UNICORN EVENT", 600, 70);
ctx.font = "900 12px system-ui";
ctx.fillText(
e < event.rebuildStart
? "THREE UNICORNS WERE IN THE ROOM. THE ROOM REGRETTED IT."
: "REBUILDING FROM SPONSOR'S CREDIT. PROMOTION REMAINS ACTIVE.",
600, 94
);
ctx.fillStyle = "rgba(5,6,12,0.82)";
roundedRect(320, 655, 560, 42, 10);
ctx.fill();
ctx.fillStyle = "#ffd166";
ctx.font = "800 12px system-ui";
ctx.fillText(
`EVENT ${String(stats.cats).padStart(2, "0")} · ${stats.voided} DRINKS VOIDED · THE DICE WERE NOT INVOLVED`,
600, 681
);
ctx.restore();
}
function drawEffects() {
if (holders.size === 2 && !reducedMotion) {
ctx.save();
ctx.globalCompositeOperation = "screen";
ctx.globalAlpha = 0.035 + Math.sin(visualTime * 8) * 0.018;
ctx.fillStyle = "#ff4fc8";
ctx.fillRect(0, 0, W, H);
ctx.globalAlpha = 0.022;
ctx.fillStyle = "#62e9ff";
ctx.fillRect((visualTime * 70) % 220 - 220, 0, 220, H);
ctx.restore();
} else if (holders.size === 1) {
ctx.save();
const glow = ctx.createRadialGradient(500, 460, 80, 600, 360, 680);
glow.addColorStop(0, "rgba(255,79,200,0)");
glow.addColorStop(1, "rgba(255,79,200,0.045)");
ctx.fillStyle = glow;
ctx.fillRect(0, 0, W, H);
ctx.restore();
}
}
function drawScene() {
ctx.setTransform(canvas.width / W, 0, 0, canvas.height / H, 0, 0);
ctx.clearRect(0, 0, W, H);
drawBackground(visualTime);
const shake = currentShake();
const sx = shake ? Math.sin(visualTime * 81) * shake : 0;
const sy = shake ? Math.cos(visualTime * 67) * shake * 0.55 : 0;
ctx.save();
ctx.translate(sx, sy);
if (event) {
drawRuins(visualTime);
drawRebuild(visualTime);
} else {
drawShop(visualTime);
}
const visible = customers
.filter(c => !(event && c.hiddenDuringEvent))
.slice()
.sort((a, b) => a.y - b.y);
for (const c of visible) drawCustomer(c);
if (event) {
for (const ghost of event.ghosts) drawCustomer(ghost, true);
drawGiantUnicorn();
}
drawParticles();
ctx.restore();
drawEffects();
drawEventOverlay();
const vignette = ctx.createRadialGradient(W / 2, H / 2, 260, W / 2, H / 2, 720);
vignette.addColorStop(0, "rgba(0,0,0,0)");
vignette.addColorStop(1, "rgba(0,0,0,0.3)");
ctx.fillStyle = vignette;
ctx.fillRect(0, 0, W, H);
}
function resizeCanvas() {
const rect = canvas.getBoundingClientRect();
const dpr = Math.min(2, window.devicePixelRatio || 1);
canvas.width = Math.max(1, Math.round(rect.width * dpr));
canvas.height = Math.max(1, Math.round(rect.height * dpr));
}
let audioContext = null;
let soundEnabled = false;
function ensureAudio() {
if (!audioContext) {
const AudioCtor = window.AudioContext || window.webkitAudioContext;
if (!AudioCtor) return null;
audioContext = new AudioCtor();
}
if (audioContext.state === "suspended") audioContext.resume();
return audioContext;
}
function tone(frequency, duration, type = "sine", volume = 0.025, offset = 0) {
if (!soundEnabled || !audioContext) return;
const now = audioContext.currentTime + offset;
const osc = audioContext.createOscillator();
const gain = audioContext.createGain();
osc.type = type;
osc.frequency.setValueAtTime(frequency, now);
gain.gain.setValueAtTime(volume, now);
gain.gain.exponentialRampToValueAtTime(0.0001, now + duration);
osc.connect(gain).connect(audioContext.destination);
osc.start(now);
osc.stop(now + duration + 0.02);
}
function playRegisterSound() {
tone(640, 0.07, "sine", 0.012);
}
function playUnicornSound() {
tone(523, 0.18, "sine", 0.022);
tone(784, 0.22, "triangle", 0.018, 0.08);
tone(1047, 0.25, "sine", 0.015, 0.16);
}
function playCatastropheSound() {
if (!soundEnabled || !audioContext) return;
const now = audioContext.currentTime;
const length = Math.floor(audioContext.sampleRate * 1.3);
const buffer = audioContext.createBuffer(1, length, audioContext.sampleRate);
const data = buffer.getChannelData(0);
for (let i = 0; i < length; i++) {
data[i] = (Math.random() * 2 - 1) * Math.pow(1 - i / length, 2.2);
}
const source = audioContext.createBufferSource();
const filter = audioContext.createBiquadFilter();
const gain = audioContext.createGain();
filter.type = "lowpass";
filter.frequency.setValueAtTime(1400, now);
filter.frequency.exponentialRampToValueAtTime(90, now + 1.2);
gain.gain.setValueAtTime(0.16, now);
gain.gain.exponentialRampToValueAtTime(0.0001, now + 1.3);
source.buffer = buffer;
source.connect(filter).connect(gain).connect(audioContext.destination);
source.start(now);
const osc = audioContext.createOscillator();
const oscGain = audioContext.createGain();
osc.type = "sawtooth";
osc.frequency.setValueAtTime(110, now);
osc.frequency.exponentialRampToValueAtTime(28, now + 1.1);
oscGain.gain.setValueAtTime(0.075, now);
oscGain.gain.exponentialRampToValueAtTime(0.0001, now + 1.15);
osc.connect(oscGain).connect(audioContext.destination);
osc.start(now);
osc.stop(now + 1.2);
tone(130, 0.8, "square", 0.035, 0.1);
tone(196, 0.7, "sawtooth", 0.025, 0.2);
}
$("speedSelect").addEventListener("change", e => {
speed = Number(e.target.value);
});
$("pauseBtn").addEventListener("click", () => {
paused = !paused;
$("pauseBtn").textContent = paused ? "Resume" : "Pause";
$("pauseBtn").setAttribute("aria-pressed", String(paused));
});
$("resetBtn").addEventListener("click", resetSimulation);
$("soundBtn").addEventListener("click", () => {
const audio = ensureAudio();
if (!audio) {
$("soundBtn").textContent = "Audio unavailable";
$("soundBtn").disabled = true;
return;
}
soundEnabled = !soundEnabled;
$("soundBtn").textContent = soundEnabled ? "Sound: on" : "Sound: off";
$("soundBtn").setAttribute("aria-pressed", String(soundEnabled));
if (soundEnabled) {
tone(440, 0.09, "sine", 0.025);
tone(660, 0.12, "sine", 0.02, 0.08);
}
});
function frame(now) {
const rawDt = Math.min(0.05, Math.max(0, (now - lastFrame) / 1000));
lastFrame = now;
if (!paused) {
visualTime += rawDt;
if (event) {
event.elapsed += rawDt;
updateParticles(rawDt);
if (event.elapsed >= event.duration) finishCatastrophe();
} else {
update(rawDt * speed);
}
}
drawScene();
uiClock += rawDt;
if (uiClock >= 0.1) {
uiClock = 0;
updateUI();
}
requestAnimationFrame(frame);
}
if (window.ResizeObserver) {
new ResizeObserver(resizeCanvas).observe(canvas);
} else {
window.addEventListener("resize", resizeCanvas);
}
for (let i = 0; i < 4; i++) spawnCustomer(true);
admitNext();
resizeCanvas();
updateUI();
requestAnimationFrame(frame);
})();
</script>
</body>
</html>
At 1×, four customers start outside, one order is completed every 2.8 seconds, and the line gradually becomes a small geometric crowd while completed customers spend about eight seconds inside displaying their drink. A normal drink gets a subdued cup; a unicorn drink produces a colored aura. One simultaneous unicorn adds a faint pink contamination to the room, while two cause the lights, building, colors, and particles to become increasingly unreliable. The arrival of a third simultaneous holder immediately destroys the shop, launches the rainbow explosion, reveals the dancing neon Unicorn, and eventually rebuilds the room without resetting the counters.
I tuned the order and brewing timers, the eight-second hold, the customer arrival rate, and the exit speed so that the previous three drinks—not two or four—overlap the next drink. The only probability-related operation is one exact six-sided roll per drink; visual randomness is separate. The catastrophe animation is timed, but the catastrophe itself is not: it can only begin when the live holder set reaches three. The service clock responds to the speed control, while the 7.2-second disaster/rebuild sequence deliberately remains at viewing speed so the payoff is still watchable at 16×.
At 1×, the first event is expected after about 3 minutes 47 seconds, but the geometric distribution is broad: there is roughly a 36% chance that no event occurs during the first 81 drinks, while a fast run can produce one much sooner. The observed unicorn rate also swings substantially early on before settling near 16.67%. The art is intentionally geometric rather than detailed, the outside line eventually bunches, and customers who re-enter after a disaster can be served again, so “customers served” is effectively a count of drinks handed over rather than unique people.