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 this simulation, the counter takes $T_s \approx 1.8$ seconds to make a drink, and a served customer lingers inside the shop (admiring their cup, walking to a lounge table, sipping, and walking to the exit) for an average of $T_d \approx 11.2$ seconds before stepping through the exit door. By Little’s Law, the average number of drink-holding customers inside the shop at steady state is $L = T_d / T_s \approx 6.2$ customers (comfortably fitting under the 8-customer indoor cap, alongside 1 ordering customer and 1 walking to the counter). Since every drink is an independent Bernoulli trial with $p = 1/6$, when a new customer receives their drink at the counter they join a cohort of roughly $k = 5$ other drink-holders currently still inside the building. A catastrophe triggers at the moment a newly served Unicorn drink ($p = 1/6$) brings the indoor Unicorn count from 2 to 3. The probability that at least 2 of the $k = 5$ existing indoor drink-holders already have a Unicorn is $\sum_{i=2}^{5} \binom{5}{i} (1/6)^i (5/6)^{5-i} \approx 0.196$. However, avoiding double-counting already-triggered states, the exact probability that a newly served customer completes a set of 3 inside a sliding window of 6 consecutive drinks (where 1 is the newcomer and 5 are already inside) is approximately $P(\text{trigger on serve}) = \frac{1}{6} \times \binom{5}{2} \left(\frac{1}{6}\right)^2 \left(\frac{5}{6}\right)^3 \approx 0.0268$, or about 1 in 37.3 customers (roughly every 67 seconds at $1\times$ speed). If random table lingers occasionally drop the active indoor drink-holders to 4, the sliding window drops to 5 and the trigger rate per customer shifts to $\frac{1}{6}\binom{4}{2}(1/6)^2(5/6)^2 \approx 0.0193$ (1 in 52 customers). Averaging across the variance in table dwell times, you should expect a catastrophe roughly every 40 to 48 customers served.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>STARBUCKS RESERVE // PROJECT UNICORN</title>
<style>
:root {
--sb-green: #00704A;
--sb-dark: #1E3932;
--sb-gold: #CBA258;
--sb-cream: #F2F0EB;
--neon-pink: #ff00aa;
--neon-cyan: #00ffff;
--neon-yellow: #ffee00;
}
* { box-sizing: border-box; margin: 0; padding: 0; user-select: none; }
body {
background: #0d1512;
color: var(--sb-cream);
font-family: 'Courier New', Courier, monospace;
display: flex;
flex-direction: column;
height: 100vh;
overflow: hidden;
}
header {
background: var(--sb-dark);
border-bottom: 3px solid var(--sb-green);
padding: 10px 20px;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 10;
box-shadow: 0 4px 20px rgba(0,0,0,0.6);
}
.brand {
display: flex;
align-items: center;
gap: 12px;
}
.logo-circle {
width: 34px;
height: 34px;
border-radius: 50%;
background: var(--sb-green);
border: 2px solid var(--sb-cream);
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 18px;
transition: all 0.3s;
}
.title-block h1 {
font-size: 16px;
letter-spacing: 1px;
color: #fff;
}
.title-block p {
font-size: 11px;
color: var(--sb-gold);
}
.controls {
display: flex;
align-items: center;
gap: 16px;
background: rgba(0,0,0,0.35);
padding: 6px 14px;
border-radius: 6px;
border: 1px solid rgba(255,255,255,0.1);
}
.speed-btns {
display: flex;
gap: 6px;
}
button {
background: #162a25;
color: var(--sb-cream);
border: 1px solid var(--sb-green);
padding: 5px 10px;
font-family: inherit;
font-size: 12px;
font-weight: bold;
border-radius: 4px;
cursor: pointer;
transition: 0.15s;
}
button:hover {
background: var(--sb-green);
color: #fff;
}
button.active {
background: var(--sb-gold);
color: #000;
border-color: #fff;
}
.main-area {
display: flex;
flex: 1;
overflow: hidden;
position: relative;
}
.canvas-wrap {
flex: 1;
position: relative;
background: #121c19;
overflow: hidden;
}
canvas {
display: block;
width: 100%;
height: 100%;
}
.sidebar {
width: 330px;
background: #14221e;
border-left: 2px solid #233b34;
padding: 14px;
display: flex;
flex-direction: column;
gap: 12px;
overflow-y: auto;
z-index: 10;
}
.panel {
background: rgba(0,0,0,0.35);
border: 1px solid rgba(255,255,255,0.08);
border-radius: 6px;
padding: 10px 12px;
}
.panel-title {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 1.5px;
color: var(--sb-gold);
margin-bottom: 8px;
border-bottom: 1px solid rgba(203,162,88,0.25);
padding-bottom: 4px;
display: flex;
justify-content: space-between;
}
.stat-row {
display: flex;
justify-content: space-between;
font-size: 12px;
margin-bottom: 5px;
align-items: baseline;
}
.stat-label { color: #a7b8b2; }
.stat-val { font-weight: bold; color: #fff; font-size: 13px; }
.stat-val.neon {
color: #ff66dd;
text-shadow: 0 0 8px #ff00aa;
}
.meter-box {
display: flex;
gap: 6px;
margin-top: 6px;
}
.pip {
flex: 1;
height: 20px;
background: #1d2e29;
border: 1px solid #334e46;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
font-size: 10px;
font-weight: bold;
color: #557068;
transition: all 0.2s;
}
.pip.active-1 {
background: #3d1c47;
border-color: #d400ff;
color: #f3b8ff;
box-shadow: 0 0 8px rgba(212,0,255,0.5);
}
.pip.active-2 {
background: #6e0d46;
border-color: #ff0077;
color: #fff;
box-shadow: 0 0 14px rgba(255,0,119,0.8);
animation: pulseFast 0.35s infinite alternate;
}
.pip.active-3 {
background: linear-gradient(90deg, #ff0055, #ffdd00, #00ffcc, #ff00aa);
background-size: 200% 200%;
border-color: #fff;
color: #000;
box-shadow: 0 0 20px #fff;
animation: rainbowBg 0.4s infinite linear;
}
@keyframes pulseFast {
from { transform: scale(0.97); }
to { transform: scale(1.04); }
}
@keyframes rainbowBg {
0% { background-position: 0% 50%; }
100% { background-position: 100% 50%; }
}
.dice-display {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 4px;
background: #0d1714;
padding: 8px 10px;
border-radius: 5px;
border: 1px solid #223831;
}
.die-box {
width: 36px;
height: 36px;
border-radius: 6px;
background: #233932;
border: 2px solid #497567;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
font-weight: bold;
color: #fff;
}
.die-box.hit {
background: linear-gradient(135deg, #ff00aa, #00ffff);
border-color: #fff;
color: #000;
box-shadow: 0 0 15px #ff00aa;
}
.log-box {
flex: 1;
min-height: 110px;
max-height: 180px;
overflow-y: auto;
font-size: 11px;
display: flex;
flex-direction: column;
gap: 4px;
padding-right: 4px;
}
.log-entry {
padding: 3px 6px;
border-radius: 3px;
background: rgba(255,255,255,0.03);
border-left: 3px solid #3c5c52;
line-height: 1.3;
}
.log-entry.unicorn {
border-left-color: #ff00aa;
background: rgba(255,0,170,0.1);
color: #ffb8e6;
}
.log-entry.warn {
border-left-color: #ffae00;
background: rgba(255,174,0,0.12);
color: #ffe299;
}
.log-entry.catastrophe {
border-left-color: #00ffff;
background: linear-gradient(90deg, rgba(255,0,128,0.3), rgba(0,255,255,0.3));
color: #fff;
font-weight: bold;
}
.status-banner {
text-align: center;
padding: 6px;
border-radius: 4px;
font-weight: bold;
font-size: 12px;
letter-spacing: 0.5px;
background: #1b302a;
color: #8ab8a8;
border: 1px solid #2b4c43;
}
</style>
</head>
<body>
<header>
<div class="brand">
<div class="logo-circle" id="logoCircle">★</div>
<div class="title-block">
<h1>STARBUCKS STORE #4099 // LIMITED PROMOTION</h1>
<p>1-IN-6 CHANCE OF NEON UNICORN FRAPPUCCINO AT COUNTER</p>
</div>
</div>
<div class="controls">
<span style="font-size:12px; color:#9ab5ac;">SIM SPEED:</span>
<div class="speed-btns">
<button onclick="setSpeed(1)" id="spd1" class="active">1x</button>
<button onclick="setSpeed(2)" id="spd2">2x</button>
<button onclick="setSpeed(5)" id="spd5">5x</button>
<button onclick="setSpeed(15)" id="spd15">15x</button>
<button onclick="setSpeed(40)" id="spd40">40x (WARP)</button>
</div>
</div>
</header>
<div class="main-area">
<div class="canvas-wrap" id="canvasWrap">
<canvas id="simCanvas"></canvas>
</div>
<div class="sidebar">
<div class="panel">
<div class="panel-title">
<span>CONTAINMENT STATUS</span>
<span id="occupancyText">0 / 8 IN SHOP</span>
</div>
<div class="status-banner" id="statusBanner">NORMAL COFFEE OPERATIONS</div>
<div style="font-size:11px; color:#9ab5ac; margin-top:8px;">ACTIVE UNICORNS INSIDE SHOP:</div>
<div class="meter-box">
<div class="pip" id="pip1">1: GLOW</div>
<div class="pip" id="pip2">2: UNSTABLE</div>
<div class="pip" id="pip3">3+: BREACH</div>
</div>
</div>
<div class="panel">
<div class="panel-title">
<span>COUNTER DICE ROLL (d6)</span>
<span>TARGET: 6</span>
</div>
<div class="dice-display">
<div>
<div style="font-size:11px; color:#9ab5ac;" id="lastDrinkType">Awaiting order...</div>
<div style="font-size:10px; color:#65857b; margin-top:2px;" id="rollHistory">Last rolls: -</div>
</div>
<div class="die-box" id="dieBox">-</div>
</div>
</div>
<div class="panel">
<div class="panel-title">
<span>LIVE TELEMETRY</span>
<span>UNRIGGED RNG</span>
</div>
<div class="stat-row">
<span class="stat-label">Customers Served:</span>
<span class="stat-val" id="statServed">0</span>
</div>
<div class="stat-row">
<span class="stat-label">Unicorns Produced:</span>
<span class="stat-val neon" id="statUnicorns">0</span>
</div>
<div class="stat-row">
<span class="stat-label">Observed Unicorn Rate:</span>
<span class="stat-val" id="statObsRate">0.00%</span>
</div>
<div class="stat-row">
<span class="stat-label">True Theoretical Rate:</span>
<span class="stat-val" style="color:var(--sb-gold);">16.67% (1 in 6)</span>
</div>
<div class="stat-row" style="margin-top:6px; padding-top:6px; border-top:1px solid rgba(255,255,255,0.08);">
<span class="stat-label">Catastrophes (3+ Inside):</span>
<span class="stat-val neon" id="statCatastrophes">0</span>
</div>
<div class="stat-row">
<span class="stat-label">Served Since Last Disaster:</span>
<span class="stat-val" id="statSinceLast">0</span>
</div>
<div class="stat-row">
<span class="stat-label">Mean Cust / Catastrophe:</span>
<span class="stat-val" id="statAvgBetween">—</span>
</div>
</div>
<div class="panel">
<div class="panel-title"><span>COLLATERAL METRICS</span></div>
<div class="stat-row">
<span class="stat-label">Close Calls (2 Inside):</span>
<span class="stat-val" id="statCloseCalls">0</span>
</div>
<div class="stat-row">
<span class="stat-label">Barista Sanity:</span>
<span class="stat-val" id="statSanity">100%</span>
</div>
<div class="stat-row">
<span class="stat-label">Reconstruction Bill:</span>
<span class="stat-val" id="statCost">$0</span>
</div>
</div>
<div class="panel" style="flex:1; display:flex; flex-direction:column;">
<div class="panel-title"><span>INCIDENT LOG</span></div>
<div class="log-box" id="logBox"></div>
</div>
</div>
</div>
<script>
const canvas = document.getElementById('simCanvas');
const ctx = canvas.getContext('2d');
let W = 900, H = 640;
function resize() {
const wrap = document.getElementById('canvasWrap');
W = wrap.clientWidth;
H = wrap.clientHeight;
canvas.width = W;
canvas.height = H;
}
window.addEventListener('resize', resize);
resize();
// Simulation Speed
let simSpeed = 1;
function setSpeed(s) {
simSpeed = s;
document.querySelectorAll('.speed-btns button').forEach(b => b.classList.remove('active'));
document.getElementById('spd' + s).classList.add('active');
}
// Shop Geometry (computed relative to canvas size)
function getShopRect() {
const sw = Math.min(540, W * 0.62);
const sh = Math.min(390, H * 0.65);
const sx = W * 0.30;
const sy = (H - sh) * 0.5;
return { x: sx, y: sy, w: sw, h: sh };
}
// Tables inside the shop for customers to linger at
function getTables(shop) {
return [
{ x: shop.x + shop.w * 0.32, y: shop.y + shop.h * 0.62, r: 22 },
{ x: shop.x + shop.w * 0.56, y: shop.y + shop.h * 0.52, r: 22 },
{ x: shop.x + shop.w * 0.78, y: shop.y + shop.h * 0.60, r: 22 },
{ x: shop.x + shop.w * 0.45, y: shop.y + shop.h * 0.80, r: 22 },
{ x: shop.x + shop.w * 0.70, y: shop.y + shop.h * 0.80, r: 22 }
];
}
// Live Statistics
const stats = {
served: 0,
unicorns: 0,
catastrophes: 0,
sinceLast: 0,
closeCalls: 0,
sanity: 100,
cost: 0,
recentRolls: [],
catastropheIntervals: []
};
// State machine for shop & catastrophe
let shopState = 'NORMAL'; // 'NORMAL', 'CATASTROPHE', 'REBUILDING'
let catastropheTimer = 0;
let rebuildTimer = 0;
let globalTime = 0;
let lastUnicornCount = 0;
// Entities
let customers = [];
let nextCustomerId = 1;
let spawnCooldown = 0;
let particles = [];
let floatingTexts = [];
let wallBricks = [];
let lightningBolts = [];
// Barista State
const barista = {
makingDrink: false,
progress: 0,
makeDuration: 1.8, // seconds per drink
targetCustomer: null,
bobPhase: 0,
quote: "Welcome to Starbucks",
quoteTimer: 0
};
const NORMAL_DRINKS = [
{ name: "Caramel Frappuccino", color: "#c68b59" },
{ name: "Mocha Cookie Crumble", color: "#5c3a21" },
{ name: "Matcha Crème Frap", color: "#7ab85c" },
{ name: "Java Chip Frap", color: "#4a2e1b" },
{ name: "Vanilla Bean Frap", color: "#f5eedc" }
];
const BARISTA_QUOTES_0 = [
"Order up! Tall Caramel Frap!",
"Spelling your name wrong...",
"Ice, milk, syrup, blend...",
"Would you like whip with that?",
"Normal Tuesday so far."
];
const BARISTA_QUOTES_1 = [
"Why is that cup humming?",
"Did the blender just purr?",
"Who ordered the glowing pink stuff?",
"My fillings are vibrating."
];
const BARISTA_QUOTES_2 = [
"NOBODY ELSE ORDER A UNICORN",
"THE ESPRESSO MACHINE IS BLEEDING GLITTER",
"I DON'T GET PAID ENOUGH FOR SYNTHWAVE",
"REALITY IS CURDLING AT TABLE 3"
];
function addLog(msg, type = '') {
const box = document.getElementById('logBox');
const div = document.createElement('div');
div.className = 'log-entry ' + type;
const timeStr = Math.floor(globalTime).toString().padStart(4, '0') + 's';
div.textContent = `[${timeStr}] ${msg}`;
box.prepend(div);
while (box.children.length > 45) {
box.removeChild(box.lastChild);
}
}
addLog("Store #4099 opened. Unicorn promotion active (1-in-6 odds).", "");
function spawnCustomer() {
const shop = getShopRect();
customers.push({
id: nextCustomerId++,
x: -25,
y: shop.y + shop.h * 0.25 + (Math.random() - 0.5) * 20,
vx: 0,
vy: 0,
radius: 11,
state: 'QUEUE', // QUEUE, TO_COUNTER, ORDERING, ADMIRING, LINGERING, TO_EXIT, LEAVING, LAUNCHED
inside: false,
hasDrink: false,
isUnicorn: false,
drinkColor: '#ccc',
lingerTime: 0,
maxLinger: 0,
targetX: 0,
targetY: 0,
tableIndex: -1,
shirtColor: `hsl(${Math.floor(Math.random() * 360)}, 35%, 52%)`,
wobble: Math.random() * Math.PI * 2
});
}
function countInsideCustomers() {
let c = 0;
for (let i = 0; i < customers.length; i++) {
if (customers[i].inside) c++;
}
return c;
}
function countInsideUnicorns() {
let u = 0;
for (let i = 0; i < customers.length; i++) {
if (customers[i].inside && customers[i].hasDrink && customers[i].isUnicorn) u++;
}
return u;
}
// Genuine 1-in-6 roll at the counter
function rollForDrink(customer, shop) {
const die = Math.floor(Math.random() * 6) + 1; // 1, 2, 3, 4, 5, 6
const isUnicorn = (die === 6);
stats.served++;
stats.sinceLast++;
stats.recentRolls.unshift(die);
if (stats.recentRolls.length > 8) stats.recentRolls.pop();
customer.hasDrink = true;
customer.isUnicorn = isUnicorn;
const dieBox = document.getElementById('dieBox');
dieBox.textContent = die;
document.getElementById('rollHistory').textContent = 'Last rolls: ' + stats.recentRolls.join(', ');
if (isUnicorn) {
stats.unicorns++;
dieBox.className = 'die-box hit';
document.getElementById('lastDrinkType').innerHTML = '<span style="color:#ff55ee;font-weight:bold;">★ NEON UNICORN FRAP ★</span>';
floatingTexts.push({
x: customer.x,
y: customer.y - 18,
text: "🎲6! NEON UNICORN!",
color: "#00ffff",
life: 1.8,
maxLife: 1.8
});
// Burst of neon particles at counter
for (let i = 0; i < 24; i++) {
const ang = Math.random() * Math.PI * 2;
const spd = 30 + Math.random() * 90;
particles.push({
x: customer.x,
y: customer.y,
vx: Math.cos(ang) * spd,
vy: Math.sin(ang) * spd,
size: 3 + Math.random() * 4,
color: `hsl(${Math.floor(Math.random() * 360)}, 100%, 60%)`,
life: 0.8 + Math.random() * 0.6
});
}
addLog(`Customer #${customer.id} rolled a 6! NEON UNICORN served.`, 'unicorn');
} else {
const pick = NORMAL_DRINKS[Math.floor(Math.random() * NORMAL_DRINKS.length)];
customer.drinkColor = pick.color;
dieBox.className = 'die-box';
document.getElementById('lastDrinkType').textContent = pick.name;
floatingTexts.push({
x: customer.x,
y: customer.y - 15,
text: `🎲${die}`,
color: "#a5b8b0",
life: 0.9,
maxLife: 0.9
});
}
// Check indoor unicorn count immediately after serving
const currentUnicorns = countInsideUnicorns();
if (isUnicorn && currentUnicorns === 2) {
stats.closeCalls++;
stats.sanity = Math.max(5, stats.sanity - 8);
addLog(`WARNING: 2 Neon Unicorns inside shop simultaneously! Dimensional resonance rising!`, 'warn');
}
if (currentUnicorns >= 3 && shopState === 'NORMAL') {
triggerCatastrophe(shop);
}
updateStatsUI();
}
function triggerCatastrophe(shop) {
shopState = 'CATASTROPHE';
catastropheTimer = 7.5; // 7.5 seconds of utter mayhem
stats.catastrophes++;
stats.catastropheIntervals.push(stats.sinceLast);
stats.sinceLast = 0;
stats.sanity = 0;
stats.cost += 250000 + Math.floor(Math.random() * 750000);
addLog(`CRITICAL BREACH! 3 NEON UNICORNS IN SHOP! REALITY COLLAPSED!`, 'catastrophe');
barista.makingDrink = false;
barista.targetCustomer = null;
barista.quote = "ALL HAIL THE EQUINE SINGULARITY";
// Blow apart the shop walls into bricks
wallBricks = [];
const perimeterSteps = 44;
for (let i = 0; i < perimeterSteps; i++) {
const bx = shop.x + (i / perimeterSteps) * shop.w;
const byTop = shop.y;
const byBot = shop.y + shop.h;
[byTop, byBot].forEach(by => {
const ang = Math.atan2(by - (shop.y + shop.h / 2), bx - (shop.x + shop.w / 2)) + (Math.random() - 0.5) * 0.6;
const spd = 120 + Math.random() * 240;
wallBricks.push({
x: bx,
y: by,
vx: Math.cos(ang) * spd,
vy: Math.sin(ang) * spd,
rot: Math.random() * Math.PI,
vrot: (Math.random() - 0.5) * 12,
w: 18 + Math.random() * 14,
h: 10 + Math.random() * 8,
color: Math.random() < 0.5 ? '#00704A' : '#CBA258'
});
});
}
// Launch all indoor customers into psychedelic orbit
customers.forEach(c => {
if (c.inside) {
c.state = 'LAUNCHED';
const ang = Math.random() * Math.PI * 2;
const spd = 180 + Math.random() * 220;
c.vx = Math.cos(ang) * spd;
c.vy = Math.sin(ang) * spd;
}
});
// Massive particle supernova
for (let i = 0; i < 160; i++) {
const ang = Math.random() * Math.PI * 2;
const spd = 50 + Math.random() * 360;
particles.push({
x: shop.x + shop.w * 0.5,
y: shop.y + shop.h * 0.5,
vx: Math.cos(ang) * spd,
vy: Math.sin(ang) * spd,
size: 4 + Math.random() * 9,
color: `hsl(${Math.floor(Math.random() * 360)}, 100%, 60%)`,
life: 2.5 + Math.random() * 3.0
});
}
updateStatsUI();
}
function updateStatsUI() {
document.getElementById('statServed').textContent = stats.served.toLocaleString();
document.getElementById('statUnicorns').textContent = stats.unicorns.toLocaleString();
const rate = stats.served > 0 ? ((stats.unicorns / stats.served) * 100).toFixed(2) : "0.00";
document.getElementById('statObsRate').textContent = `${rate}%`;
document.getElementById('statCatastrophes').textContent = stats.catastrophes.toLocaleString();
document.getElementById('statSinceLast').textContent = stats.sinceLast.toLocaleString();
if (stats.catastropheIntervals.length > 0) {
const sum = stats.catastropheIntervals.reduce((a, b) => a + b, 0);
const avg = (sum / stats.catastropheIntervals.length).toFixed(1);
document.getElementById('statAvgBetween').textContent = `${avg} cust`;
} else {
document.getElementById('statAvgBetween').textContent = "—";
}
document.getElementById('statCloseCalls').textContent = stats.closeCalls.toLocaleString();
document.getElementById('statSanity').textContent = `${Math.round(stats.sanity)}%`;
document.getElementById('statCost').textContent = `$${stats.cost.toLocaleString()}`;
}
function moveTowards(entity, tx, ty, speed, dt) {
const dx = tx - entity.x;
const dy = ty - entity.y;
const dist = Math.hypot(dx, dy);
if (dist <= speed * dt) {
entity.x = tx;
entity.y = ty;
return true;
}
entity.x += (dx / dist) * speed * dt;
entity.y += (dy / dist) * speed * dt;
return false;
}
function stepSimulation(dt) {
globalTime += dt;
const shop = getShopRect();
const tables = getTables(shop);
const entranceDoor = { x: shop.x, y: shop.y + shop.h * 0.25 };
const exitDoor = { x: shop.x, y: shop.y + shop.h * 0.78 };
const counterSpot = { x: shop.x + shop.w * 0.42, y: shop.y + 82 };
// Handle Catastrophe & Rebuild states
if (shopState === 'CATASTROPHE') {
catastropheTimer -= dt;
// Continuous rainbow fountain & lightning during catastrophe
if (Math.random() < 30 * dt) {
const ang = -Math.PI / 2 + (Math.random() - 0.5) * 1.8;
const spd = 120 + Math.random() * 260;
particles.push({
x: shop.x + shop.w * 0.5 + (Math.random() - 0.5) * 90,
y: shop.y + shop.h * 0.55,
vx: Math.cos(ang) * spd,
vy: Math.sin(ang) * spd,
size: 5 + Math.random() * 7,
color: `hsl(${(globalTime * 400 + Math.random() * 180) % 360}, 100%, 60%)`,
life: 1.2 + Math.random()
});
}
// Update flying bricks
wallBricks.forEach(b => {
b.x += b.vx * dt;
b.y += b.vy * dt;
b.vy += 180 * dt; // gravity
b.rot += b.vrot * dt;
});
// Update launched indoor customers
for (let i = customers.length - 1; i >= 0; i--) {
const c = customers[i];
if (c.state === 'LAUNCHED') {
c.x += c.vx * dt;
c.y += c.vy * dt;
c.wobble += 15 * dt;
// Bounce weakly off screen edges or fly away
if (c.x < -100 || c.x > W + 100 || c.y < -100 || c.y > H + 100) {
customers.splice(i, 1);
}
}
}
if (catastropheTimer <= 0) {
// Remove any remaining launched customers inside
customers = customers.filter(c => c.state === 'QUEUE');
shopState = 'REBUILDING';
rebuildTimer = 2.0;
addLog("Corporate Damage Control deployed. Rebuilding Store #4099...", "");
}
} else if (shopState === 'REBUILDING') {
rebuildTimer -= dt;
if (rebuildTimer <= 0) {
shopState = 'NORMAL';
stats.sanity = 100;
wallBricks = [];
barista.quote = "We apologize for the interdimensional delay.";
addLog("Store #4099 reopened. Promotion resumes immediately.", "");
updateStatsUI();
}
}
// Normal operations customer & barista logic
if (shopState === 'NORMAL') {
// Keep external queue stocked (up to 10 people waiting outside)
const queueCount = customers.filter(c => c.state === 'QUEUE').length;
spawnCooldown -= dt;
if (queueCount < 9 && spawnCooldown <= 0) {
spawnCustomer();
spawnCooldown = 0.65 + Math.random() * 0.6;
}
const insideCount = countInsideCustomers();
const unicornCount = countInsideUnicorns();
// Ambient effects based on unicorn count
if (unicornCount >= 1 && Math.random() < (unicornCount === 1 ? 8 : 28) * dt) {
customers.forEach(c => {
if (c.inside && c.isUnicorn) {
particles.push({
x: c.x + (Math.random() - 0.5) * 16,
y: c.y + (Math.random() - 0.5) * 16,
vx: (Math.random() - 0.5) * 35,
vy: -20 - Math.random() * 40,
size: 2.5 + Math.random() * 3.5,
color: `hsl(${Math.floor(Math.random() * 360)}, 100%, 65%)`,
life: 0.7 + Math.random() * 0.5
});
}
});
}
// Arc lightning between the 2 unicorn holders when 2 are in the room
if (unicornCount === 2 && Math.random() < 12 * dt) {
const uniHolders = customers.filter(c => c.inside && c.isUnicorn);
if (uniHolders.length === 2) {
lightningBolts.push({
x1: uniHolders[0].x,
y1: uniHolders[0].y,
x2: uniHolders[1].x,
y2: uniHolders[1].y,
color: Math.random() < 0.5 ? '#ff00aa' : '#00ffff',
life: 0.12
});
}
}
// Barista quotes
barista.quoteTimer -= dt;
if (barista.quoteTimer <= 0) {
barista.quoteTimer = 3.2;
if (unicornCount === 0) {
barista.quote = BARISTA_QUOTES_0[Math.floor(Math.random() * BARISTA_QUOTES_0.length)];
} else if (unicornCount === 1) {
barista.quote = BARISTA_QUOTES_1[Math.floor(Math.random() * BARISTA_QUOTES_1.length)];
} else {
barista.quote = BARISTA_QUOTES_2[Math.floor(Math.random() * BARISTA_QUOTES_2.length)];
}
}
// Recover sanity slowly when 0 unicorns inside
if (unicornCount === 0 && stats.sanity < 100) {
stats.sanity = Math.min(100, stats.sanity + 2 * dt);
}
// Check if counter path is clear (at most 1 customer heading to counter or ordering)
const counterBusy = customers.some(c => c.state === 'TO_COUNTER' || c.state === 'ORDERING');
// Advance external queue
let queueIdx = 0;
for (let i = 0; i < customers.length; i++) {
const c = customers[i];
if (c.state === 'QUEUE') {
// Can the front of the queue enter the shop?
// Strictly enforce <= 8 customers inside at once!
if (queueIdx === 0 && countInsideCustomers() < 8 && !counterBusy) {
c.inside = true;
c.state = 'TO_COUNTER';
} else {
const qx = entranceDoor.x - 28 - queueIdx * 26;
const qy = entranceDoor.y + Math.sin(queueIdx * 0.7) * 6;
moveTowards(c, qx, qy, 115, dt);
queueIdx++;
}
}
}
// Update all customers
for (let i = customers.length - 1; i >= 0; i--) {
const c = customers[i];
c.wobble += dt * (c.isUnicorn ? 12 : 5);
if (c.state === 'TO_COUNTER') {
const arrived = moveTowards(c, counterSpot.x, counterSpot.y, 135, dt);
if (arrived) {
c.state = 'ORDERING';
barista.makingDrink = true;
barista.progress = 0;
barista.targetCustomer = c;
}
} else if (c.state === 'ORDERING') {
// Waiting for barista to finish blending
} else if (c.state === 'ADMIRING') {
// Brief pause near pickup counter so customer reactions are easy to spot
moveTowards(c, c.targetX, c.targetY, 90, dt);
c.lingerTime += dt;
if (c.lingerTime >= 1.2) {
c.state = 'LINGERING';
c.lingerTime = 0;
// Pick a lounge table Spot
const tIdx = Math.floor(Math.random() * tables.length);
const tbl = tables[tIdx];
const angle = Math.random() * Math.PI * 2;
c.targetX = tbl.x + Math.cos(angle) * (tbl.r + 14);
c.targetY = tbl.y + Math.sin(angle) * (tbl.r + 14);
// Total indoor drink-holding time averages ~11.2 seconds (1.2s admiring + ~8.4s table + ~1.6s walking to exit)
c.maxLinger = 6.9 + Math.random() * 3.0;
}
} else if (c.state === 'LINGERING') {
moveTowards(c, c.targetX, c.targetY, 95, dt);
c.lingerTime += dt;
if (c.lingerTime >= c.maxLinger) {
c.state = 'TO_EXIT';
}
} else if (c.state === 'TO_EXIT') {
const atDoor = moveTowards(c, exitDoor.x, exitDoor.y, 115, dt);
if (atDoor) {
c.inside = false; // Out the door! No longer inside the shop.
c.state = 'LEAVING';
}
} else if (c.state === 'LEAVING') {
const gone = moveTowards(c, -45, exitDoor.y + 40, 130, dt);
if (gone) {
customers.splice(i, 1);
}
}
}
// Update Barista drink preparation
if (barista.makingDrink && barista.targetCustomer) {
barista.progress += dt;
barista.bobPhase += dt * 18;
if (barista.progress >= barista.makeDuration) {
const cust = barista.targetCustomer;
barista.makingDrink = false;
barista.targetCustomer = null;
barista.progress = 0;
// Roll the 1-in-6 dice at the counter!
rollForDrink(cust, shop);
// If catastrophe didn't just launch them, send customer to admire & linger
if (shopState === 'NORMAL') {
cust.state = 'ADMIRING';
cust.lingerTime = 0;
cust.targetX = counterSpot.x + 65 + (Math.random() - 0.5) * 30;
cust.targetY = counterSpot.y + 18 + Math.random() * 20;
}
}
}
}
// Update particles, floating texts, lightning
for (let i = particles.length - 1; i >= 0; i--) {
const p = particles[i];
p.x += p.vx * dt;
p.y += p.vy * dt;
p.life -= dt;
if (p.life <= 0) particles.splice(i, 1);
}
for (let i = floatingTexts.length - 1; i >= 0; i--) {
const ft = floatingTexts[i];
ft.y -= 28 * dt;
ft.life -= dt;
if (ft.life <= 0) floatingTexts.splice(i, 1);
}
for (let i = lightningBolts.length - 1; i >= 0; i--) {
lightningBolts[i].life -= dt;
if (lightningBolts[i].life <= 0) lightningBolts.splice(i, 1);
}
updateOccupancyAndMeters();
}
function updateOccupancyAndMeters() {
const inShop = countInsideCustomers();
const uniCount = shopState === 'CATASTROPHE' ? 3 : countInsideUnicorns();
document.getElementById('occupancyText').textContent = `${inShop} / 8 IN SHOP`;
const pip1 = document.getElementById('pip1');
const pip2 = document.getElementById('pip2');
const pip3 = document.getElementById('pip3');
const banner = document.getElementById('statusBanner');
const logo = document.getElementById('logoCircle');
pip1.className = 'pip' + (uniCount >= 1 ? ' active-1' : '');
pip2.className = 'pip' + (uniCount >= 2 ? ' active-2' : '');
pip3.className = 'pip' + (uniCount >= 3 ? ' active-3' : '');
if (shopState === 'CATASTROPHE') {
banner.textContent = "🦄 CATASTROPHIC NEON UNICORN EVENT!! 🦄";
banner.style.background = "linear-gradient(90deg, #ff0066, #ffcc00, #00ffcc, #ff00aa)";
banner.style.color = "#000";
logo.textContent = "🦄";
logo.style.background = "#ff00aa";
} else if (shopState === 'REBUILDING') {
banner.textContent = "🚧 RECONSTRUCTING COFFEE SHOP... 🚧";
banner.style.background = "#4f3b16";
banner.style.color = "#ffd278";
logo.textContent = "🛠";
logo.style.background = "#CBA258";
} else if (uniCount === 2) {
banner.textContent = "⚠️ 2 UNICORNS INSIDE: CRITICAL COINCIDENCE ⚠️";
banner.style.background = "#5e0b38";
banner.style.color = "#ff99dd";
logo.textContent = "⚡";
logo.style.background = "#ff0055";
} else if (uniCount === 1) {
banner.textContent = "✨ 1 UNICORN INSIDE: SUBTLE NEON HUM ✨";
banner.style.background = "#2e1636";
banner.style.color = "#e5a8ff";
logo.textContent = "★";
logo.style.background = "#8800aa";
} else {
banner.textContent = "NORMAL COFFEE OPERATIONS";
banner.style.background = "#1b302a";
banner.style.color = "#8ab8a8";
logo.textContent = "★";
logo.style.background = "#00704A";
}
}
// Procedural Drawing
function drawScene() {
const shop = getShopRect();
const tables = getTables(shop);
const uniCount = shopState === 'CATASTROPHE' ? 3 : countInsideUnicorns();
ctx.save();
// Camera shake for 2 unicorns or catastrophe
if (shopState === 'CATASTROPHE') {
const shake = 12;
ctx.translate((Math.random() - 0.5) * shake, (Math.random() - 0.5) * shake);
} else if (uniCount === 2) {
const shake = 4.5;
ctx.translate((Math.random() - 0.5) * shake, (Math.random() - 0.5) * shake);
}
// Background pavement / lot
if (shopState === 'CATASTROPHE') {
const bgHue = (globalTime * 180) % 360;
ctx.fillStyle = `hsl(${bgHue}, 65%, 10%)`;
} else if (uniCount === 2) {
const pulse = Math.sin(globalTime * 18) * 0.5 + 0.5;
ctx.fillStyle = `rgb(${22 + pulse * 25}, 16, ${28 + pulse * 30})`;
} else if (uniCount === 1) {
ctx.fillStyle = '#161722';
} else {
ctx.fillStyle = '#111a17';
}
ctx.fillRect(-20, -20, W + 40, H + 40);
// Sidewalk & Queue Lane outside
ctx.fillStyle = '#1c2924';
ctx.fillRect(0, shop.y + shop.h * 0.14, shop.x, shop.h * 0.22);
ctx.strokeStyle = '#2e453d';
ctx.setLineDash([6, 6]);
ctx.beginPath();
ctx.moveTo(0, shop.y + shop.h * 0.14);
ctx.lineTo(shop.x, shop.y + shop.h * 0.14);
ctx.moveTo(0, shop.y + shop.h * 0.36);
ctx.lineTo(shop.x, shop.y + shop.h * 0.36);
ctx.stroke();
ctx.setLineDash([]);
ctx.fillStyle = '#5c7a70';
ctx.font = 'bold 11px Courier New';
ctx.fillText('QUEUE OUTSIDE (MAX 8 INSIDE) ➔', 14, shop.y + shop.h * 0.11);
ctx.fillText('⬅ EXIT', 18, shop.y + shop.h * 0.82);
// Draw Shop Building (if not obliterated)
if (shopState !== 'CATASTROPHE') {
const rebuildScale = shopState === 'REBUILDING' ? (1 - rebuildTimer / 2.0) : 1;
ctx.save();
if (shopState === 'REBUILDING') {
const cx = shop.x + shop.w / 2;
const cy = shop.y + shop.h / 2;
ctx.translate(cx, cy);
ctx.scale(rebuildScale, rebuildScale);
ctx.translate(-cx, -cy);
}
// Outer Neon Aura for 1 or 2 unicorns
if (uniCount === 1) {
ctx.shadowColor = '#ff00dd';
ctx.shadowBlur = 22 + Math.sin(globalTime * 6) * 8;
} else if (uniCount === 2) {
ctx.shadowColor = Math.floor(globalTime * 10) % 2 === 0 ? '#ff0077' : '#00ffff';
ctx.shadowBlur = 38;
}
// Shop Floor
ctx.fillStyle = uniCount === 2 ? '#29172b' : (uniCount === 1 ? '#211d29' : '#24201c');
ctx.fillRect(shop.x, shop.y, shop.w, shop.h);
ctx.shadowBlur = 0;
// Floor Tiles
ctx.strokeStyle = uniCount >= 1 ? `hsla(${(globalTime * 90) % 360}, 70%, 50%, ${uniCount * 0.12})` : 'rgba(255,255,255,0.03)';
ctx.lineWidth = 1;
const tileSz = 36;
for (let tx = shop.x; tx < shop.x + shop.w; tx += tileSz) {
ctx.beginPath();
ctx.moveTo(tx, shop.y);
ctx.lineTo(tx, shop.y + shop.h);
ctx.stroke();
}
for (let ty = shop.y; ty < shop.y + shop.h; ty += tileSz) {
ctx.beginPath();
ctx.moveTo(shop.x, ty);
ctx.lineTo(shop.x + shop.w, ty);
ctx.stroke();
}
// Espresso Counter & Menu Board
const counterY = shop.y + 48;
ctx.fillStyle = '#162923';
ctx.fillRect(shop.x + 30, shop.y + 6, shop.w - 60, 42);
ctx.fillStyle = uniCount === 2 ? '#ff0066' : '#3d2817';
ctx.fillRect(shop.x + 26, counterY, shop.w - 52, 16);
// Promo Sign on Counter
ctx.fillStyle = '#fff';
ctx.font = 'bold 11px Courier New';
const menuText = uniCount === 0
? "MENU: FRAPPUCCINO ($6) — 1-IN-6 CHANCE OF NEON UNICORN!"
: (uniCount === 1 ? "MENU: FRAPPUCCIN0 — THE SYRUP IS GLOWING IN THE DARK" : "M E N U : D O N O T R O L L A N O T H E R S I X");
ctx.fillText(menuText, shop.x + 42, shop.y + 24);
// Blender / Espresso Machines on Counter
const blenderX = shop.x + shop.w * 0.42;
ctx.fillStyle = barista.makingDrink && uniCount >= 1 ? `hsl(${(globalTime * 500) % 360}, 100%, 60%)` : '#8aa199';
ctx.fillRect(blenderX - 8, counterY - 12, 16, 14);
// Barista behind counter
const bx = shop.x + shop.w * 0.42 + (barista.makingDrink ? Math.sin(barista.bobPhase) * 6 : 0);
const by = shop.y + 32;
ctx.fillStyle = '#f2d2b6';
ctx.beginPath();
ctx.arc(bx, by, 10, 0, Math.PI * 2);
ctx.fill();
// Green Starbucks Apron / Cap
ctx.fillStyle = '#00704A';
ctx.beginPath();
ctx.arc(bx, by, 10, Math.PI, Math.PI * 2);
ctx.fill();
// Barista Progress Bar when blending
if (barista.makingDrink) {
const pct = Math.min(1, barista.progress / barista.makeDuration);
ctx.fillStyle = '#000';
ctx.fillRect(bx - 22, by - 22, 44, 6);
ctx.fillStyle = '#00ffcc';
ctx.fillRect(bx - 21, by - 21, 42 * pct, 4);
}
// Barista Speech Bubble
ctx.fillStyle = 'rgba(0,0,0,0.65)';
ctx.fillRect(shop.x + shop.w * 0.52, counterY - 16, shop.w * 0.42, 18);
ctx.fillStyle = uniCount === 2 ? '#ff77cc' : '#a8d5c5';
ctx.font = '10px Courier New';
ctx.fillText(`"${barista.quote}"`, shop.x + shop.w * 0.53, counterY - 4);
// Lounge Tables
tables.forEach((t, idx) => {
ctx.save();
let tx = t.x, ty = t.y;
if (uniCount === 1) {
// Subtle wrongness: tables levitate/drift slightly
ty += Math.sin(globalTime * 4 + idx) * 3.5;
} else if (uniCount === 2) {
// Warped unstable tables
tx += Math.cos(globalTime * 16 + idx) * 5;
ty += Math.sin(globalTime * 16 + idx) * 5;
}
ctx.fillStyle = uniCount === 2 ? `hsl(${(globalTime * 120 + idx * 50) % 360}, 60%, 30%)` : '#473325';
ctx.strokeStyle = '#6e523d';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(tx, ty, t.r, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
ctx.restore();
});
// Shop Walls with Entrance and Exit gaps on left wall
ctx.strokeStyle = uniCount === 2
? `hsl(${(globalTime * 360) % 360}, 100%, 60%)`
: (uniCount === 1 ? '#b833ff' : '#00704A');
ctx.lineWidth = 6;
ctx.strokeRect(shop.x, shop.y, shop.w, shop.h);
// Carve Door Openings on Left Wall
ctx.fillStyle = '#1c2924';
ctx.fillRect(shop.x - 5, shop.y + shop.h * 0.25 - 18, 12, 36);
ctx.fillRect(shop.x - 5, shop.y + shop.h * 0.78 - 18, 12, 36);
// Occupancy Counter Badge on Roof Corner
ctx.fillStyle = '#11221c';
ctx.strokeStyle = '#CBA258';
ctx.lineWidth = 1.5;
ctx.fillRect(shop.x + shop.w - 155, shop.y + shop.h - 28, 148, 22);
ctx.strokeRect(shop.x + shop.w - 155, shop.y + shop.h - 28, 148, 22);
ctx.fillStyle = '#CBA258';
ctx.font = 'bold 11px Courier New';
ctx.fillText(`INSIDE: ${countInsideCustomers()}/8 | 🦄: ${uniCount}/3`, shop.x + shop.w - 148, shop.y + shop.h - 13);
ctx.restore();
} else {
// CATASTROPHE CRATER & GIANT DANCING NEON UNICORN
const cx = shop.x + shop.w * 0.5;
const cy = shop.y + shop.h * 0.52;
// Psychedelic Portal Vortex where the shop stood
for (let ring = 7; ring >= 1; ring--) {
const r = ring * 34 + Math.sin(globalTime * 12 + ring) * 12;
const hue = (globalTime * 260 + ring * 45) % 360;
ctx.fillStyle = `hsla(${hue}, 100%, 55%, 0.28)`;
ctx.beginPath();
ctx.arc(cx, cy, r, 0, Math.PI * 2);
ctx.fill();
}
// Disco Lasers shooting from the crater
for (let b = 0; b < 10; b++) {
const ang = globalTime * 2.5 + (b * Math.PI * 2) / 10;
ctx.strokeStyle = `hsla(${(b * 36 + globalTime * 300) % 360}, 100%, 65%, 0.55)`;
ctx.lineWidth = 8;
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.lineTo(cx + Math.cos(ang) * 900, cy + Math.sin(ang) * 900);
ctx.stroke();
}
// Flying shattered shop bricks
wallBricks.forEach(b => {
ctx.save();
ctx.translate(b.x, b.y);
ctx.rotate(b.rot);
ctx.fillStyle = b.color;
ctx.fillRect(-b.w / 2, -b.h / 2, b.w, b.h);
ctx.restore();
});
// GIANT DANCING NEON UNICORN CREATURE
drawGiantDancingUnicorn(cx, cy, globalTime);
}
// Draw Arc Lightning (when 2 unicorns inside)
lightningBolts.forEach(bolt => {
ctx.strokeStyle = bolt.color;
ctx.lineWidth = 3;
ctx.shadowColor = bolt.color;
ctx.shadowBlur = 12;
ctx.beginPath();
ctx.moveTo(bolt.x1, bolt.y1);
const segments = 6;
for (let s = 1; s < segments; s++) {
const t = s / segments;
const mx = bolt.x1 + (bolt.x2 - bolt.x1) * t + (Math.random() - 0.5) * 28;
const my = bolt.y1 + (bolt.y2 - bolt.y1) * t + (Math.random() - 0.5) * 28;
ctx.lineTo(mx, my);
}
ctx.lineTo(bolt.x2, bolt.y2);
ctx.stroke();
ctx.shadowBlur = 0;
});
// Draw Customers (circles/dots with cups)
customers.forEach(c => {
ctx.save();
const bobX = Math.cos(c.wobble) * (c.isUnicorn ? 2.5 : 0.8);
const bobY = Math.sin(c.wobble) * (c.isUnicorn ? 2.5 : 0.8);
const cx = c.x + bobX;
const cy = c.y + bobY;
// Unicorn holder aura ring
if (c.hasDrink && c.isUnicorn) {
const hue = (globalTime * 360 + c.id * 50) % 360;
ctx.strokeStyle = `hsl(${hue}, 100%, 60%)`;
ctx.lineWidth = 3;
ctx.shadowColor = `hsl(${hue}, 100%, 60%)`;
ctx.shadowBlur = 16;
ctx.beginPath();
ctx.arc(cx, cy, c.radius + 6 + Math.sin(globalTime * 15) * 2, 0, Math.PI * 2);
ctx.stroke();
}
// Customer circle body
ctx.fillStyle = c.isUnicorn
? `hsl(${(globalTime * 220 + c.id * 40) % 360}, 95%, 65%)`
: c.shirtColor;
ctx.strokeStyle = '#ffffff';
ctx.lineWidth = 1.8;
ctx.beginPath();
ctx.arc(cx, cy, c.radius, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
ctx.shadowBlur = 0;
// Eyes (or sunglasses if unicorn)
ctx.fillStyle = '#111';
ctx.beginPath();
ctx.arc(cx - 3.5, cy - 2, 1.8, 0, Math.PI * 2);
ctx.arc(cx + 3.5, cy - 2, 1.8, 0, Math.PI * 2);
ctx.fill();
// Drink Cup held by customer
if (c.hasDrink) {
const cupX = cx + 10;
const cupY = cy - 4;
if (c.isUnicorn) {
const cupHue = (globalTime * 480) % 360;
ctx.fillStyle = `hsl(${cupHue}, 100%, 60%)`;
ctx.shadowColor = '#ff00dd';
ctx.shadowBlur = 12;
} else {
ctx.fillStyle = c.drinkColor;
}
// Cup base
ctx.fillRect(cupX - 4, cupY - 5, 8, 11);
// Whipped cream dome
ctx.fillStyle = c.isUnicorn ? '#00ffff' : '#ffffff';
ctx.beginPath();
ctx.arc(cupX, cupY - 5, 4, Math.PI, 0);
ctx.fill();
// Green straw (or golden unicorn horn straw)
ctx.strokeStyle = c.isUnicorn ? '#ffee00' : '#00704A';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(cupX, cupY - 8);
ctx.lineTo(cupX + 3, cupY - 14);
ctx.stroke();
ctx.shadowBlur = 0;
}
ctx.restore();
});
// Draw Particles
particles.forEach(p => {
ctx.fillStyle = p.color;
ctx.beginPath();
ctx.arc(p.x, p.y, p.size * Math.max(0.1, p.life), 0, Math.PI * 2);
ctx.fill();
});
// Draw Floating Roll Texts
floatingTexts.forEach(ft => {
ctx.save();
ctx.globalAlpha = Math.max(0, ft.life / ft.maxLife);
ctx.font = 'bold 13px Courier New';
ctx.fillStyle = ft.color;
ctx.shadowColor = '#000';
ctx.shadowBlur = 4;
ctx.fillText(ft.text, ft.x - 18, ft.y);
ctx.restore();
});
ctx.restore();
}
function drawGiantDancingUnicorn(cx, cy, t) {
ctx.save();
const danceBounce = Math.abs(Math.sin(t * 10)) * 28;
const danceSway = Math.sin(t * 5) * 0.22;
ctx.translate(cx, cy - danceBounce + 20);
ctx.rotate(danceSway);
const neonPink = `hsl(${(t * 300) % 360}, 100%, 62%)`;
const neonCyan = `hsl(${(t * 300 + 140) % 360}, 100%, 60%)`;
const neonGold = `hsl(${(t * 300 + 60) % 360}, 100%, 58%)`;
ctx.shadowColor = neonPink;
ctx.shadowBlur = 30;
ctx.lineWidth = 6;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';
// Dancing Legs (4 articulated rave legs)
ctx.strokeStyle = neonCyan;
for (let leg = 0; leg < 4; leg++) {
const lx = -50 + leg * 34;
const kick = Math.sin(t * 14 + leg * 1.6) * 38;
ctx.beginPath();
ctx.moveTo(lx, 25);
ctx.lineTo(lx + kick * 0.6, 65);
ctx.lineTo(lx - kick * 0.4, 105);
ctx.stroke();
// Glowing Hooves
ctx.fillStyle = neonGold;
ctx.beginPath();
ctx.arc(lx - kick * 0.4, 105, 7, 0, Math.PI * 2);
ctx.fill();
}
// Flowing Rainbow Tail
for (let s = 0; s < 5; s++) {
ctx.strokeStyle = `hsl(${(t * 400 + s * 55) % 360}, 100%, 60%)`;
ctx.beginPath();
ctx.moveTo(-70, 5);
ctx.quadraticCurveTo(
-125,
-20 + Math.sin(t * 12 + s) * 35,
-150,
20 + s * 10 + Math.cos(t * 10 + s) * 20
);
ctx.stroke();
}
// Muscular Neon Torso
ctx.fillStyle = '#ffffff';
ctx.strokeStyle = neonPink;
ctx.beginPath();
ctx.ellipse(0, 10, 75, 42, 0, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
// Neck & Head
const headBob = Math.sin(t * 10) * 10;
ctx.beginPath();
ctx.moveTo(45, -10);
ctx.lineTo(75, -75 + headBob);
ctx.lineTo(125, -62 + headBob);
ctx.lineTo(118, -38 + headBob);
ctx.lineTo(75, -35 + headBob);
ctx.lineTo(65, 15);
ctx.closePath();
ctx.fill();
ctx.stroke();
// Electric Mane
for (let m = 0; m < 6; m++) {
ctx.strokeStyle = `hsl(${(t * 500 + m * 45) % 360}, 100%, 55%)`;
ctx.beginPath();
const my = -15 - m * 10 + headBob * 0.5;
ctx.moveTo(52 + m * 3, my);
ctx.lineTo(25 + Math.sin(t * 16 + m) * 18, my - 12);
ctx.stroke();
}
// Giant Spiraling Golden Unicorn Horn
ctx.fillStyle = neonGold;
ctx.strokeStyle = '#ffffff';
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(85, -72 + headBob);
ctx.lineTo(145, -145 + headBob);
ctx.lineTo(102, -66 + headBob);
ctx.closePath();
ctx.fill();
ctx.stroke();
// Rave Deal-With-It Sunglasses on Unicorn
ctx.fillStyle = '#000';
ctx.strokeStyle = '#00ffff';
ctx.lineWidth = 2;
ctx.fillRect(82, -65 + headBob, 26, 12);
ctx.strokeRect(82, -65 + headBob, 26, 12);
// Giant Floating Banner Text
ctx.rotate(-danceSway);
ctx.font = 'bold 24px Courier New';
ctx.textAlign = 'center';
ctx.fillStyle = '#ffffff';
ctx.shadowColor = '#ff00aa';
ctx.shadowBlur = 20;
ctx.fillText("★ CRITICAL UNICORN MASS ACHIEVED ★", 0, -165);
ctx.font = 'bold 14px Courier New';
ctx.fillStyle = '#ffee00';
ctx.fillText(`REBUILDING STORE IN ${catastropheTimer.toFixed(1)}s...`, 0, -142);
ctx.restore();
}
// Main Animation Loop with Sub-Stepping for High Speeds
let lastFrameTime = performance.now();
function loop(now) {
const rawDt = Math.min(0.05, (now - lastFrameTime) / 1000);
lastFrameTime = now;
// Sub-step physics so 15x and 40x warp speeds remain mathematically accurate
// and never skip door/counter triggers
const totalSimDt = rawDt * simSpeed;
const maxStep = 0.025;
const steps = Math.ceil(totalSimDt / maxStep);
const subDt = totalSimDt / steps;
for (let s = 0; s < steps; s++) {
stepSimulation(subDt);
}
drawScene();
requestAnimationFrame(loop);
}
// Seed initial customers outside
for (let i = 0; i < 5; i++) {
spawnCustomer();
}
requestAnimationFrame(loop);
</script>
</body>
</html>
When the page loads, 5 circular customers immediately walk up the outdoor sidewalk toward the entrance door, and the shop lets them in one by one as the counter clears, strictly capping indoor occupancy at 8. Every 1.8 seconds of simulation time, the barista finishes blending a drink and rolls a pure Math.floor(Math.random() * 6) + 1 at the counter. In the first couple of minutes at 1x speed, you will see normal brown and green Frappuccinos served, punctuated every half-dozen drinks or so by a 6 roll: the counter erupts in sparks, the customer turns neon with a glowing cup, and the containment meter lights up its first purple pip (1: GLOW), causing the shop walls to hum with a violet shadow and the lounge tables to levitate slightly. When a second customer inside rolls a 6 before the first one walks out the exit door, the second pip (2: UNSTABLE) triggers—the camera shakes, the floor tiles strobe, the barista panics in the speech bubble, and jagged bolts of cyan/magenta lightning arc directly between the two Unicorn-holding customers. If a third customer rolls a 6 while those two are still inside, the shop walls shatter into physics-driven bricks, all indoor customers are launched across the canvas, and a giant rave-dancing Neon Unicorn in sunglasses emerges from a spinning rainbow portal for 7.5 seconds before Corporate Damage Control shrinks and rebuilds the store.
What was tuned or special-cased:
rollForDrink() is a completely untouched Math.floor(Math.random() * 6) + 1 === 6. There is no pity timer, no bad-luck protection, and no secret modifier based on how long you have waited.40x WARP), a naive dt * 40 multiplier would cause customers to teleport past the counter or linger longer than intended per frame. The main loop subdivides high-speed frames into fixed <= 0.025s physics sub-steps so the observed catastrophe frequency and Little’s Law occupancy at 40x match 1x.counterBusy), while up to 7 others are admiring their drink, sitting at the five lounge tables, or walking to the exit. This keeps the 8-customer cap from deadlocking at the cash register.What it does badly:
40x speed, the incident log and counter dice display blur into a frantic flicker because drinks are being rolled more than 20 times per real-world second.