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 my timings the counter takes 2.0 sim-seconds per drink and a customer keeps holding that drink inside for about 7.5 sim-seconds (≈1s walking to a table plus 6.5s sipping), so any Neon Unicorn overlaps roughly the next 3 drinks served after it (at +2s, +4s and +6s; the +8s drink arrives just after it has left). For a given unicorn to become a catastrophe it therefore needs at least 2 more unicorns among those next 3 drinks, which for a fair 1-in-6 roll is C(3,2)·(1/6)²·(5/6) + (1/6)³ = 16/216 ≈ 7.4%, and the true chance is a little better than that because it can also pair up with a unicorn served just before it that is still in the room — call it ~9–10% per unicorn. Since unicorns themselves arrive once every ~6 customers, you expect roughly 6 ÷ 0.09 ≈ 65–70 customers served between catastrophes, i.e. about 2–2.5 minutes at 1× speed (30 drinks/minute), or ~20 seconds at 8× — with big variance, because genuinely independent dice can also give you two disasters almost back-to-back or a long, smug drought.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MOONBUCKS — Neon Unicorn Frappuccino Event Simulator</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
background: #0d0716; color: #f3eaff;
font-family: "Trebuchet MS", Verdana, system-ui, sans-serif;
min-height: 100vh; padding: 14px;
background-image: radial-gradient(circle at 20% 10%, #241040 0, transparent 45%),
radial-gradient(circle at 85% 90%, #07332b 0, transparent 40%);
}
header { text-align:center; margin-bottom:10px; }
header h1 { font-size: 26px; letter-spacing: 1px; color:#7dffce; text-shadow: 0 0 14px #00c853; }
header h1 .pink { color:#ff7df0; text-shadow: 0 0 14px #ff2fd6; }
header p { color:#c9b8e8; font-size: 13px; margin-top:3px; }
#wrap { display:flex; gap:14px; max-width:1280px; margin:0 auto; align-items:flex-start; flex-wrap:wrap; justify-content:center; }
#stage { flex: 1 1 700px; max-width: 980px; }
canvas {
width:100%; height:auto; display:block; border-radius:14px;
border:3px solid #2e7d5b; background:#17102a;
box-shadow: 0 0 34px rgba(0,255,170,.18), 0 10px 30px rgba(0,0,0,.5);
}
#controls {
display:flex; gap:10px; align-items:center; flex-wrap:wrap;
background:#1c1233; border:1px solid #3d2a66; border-radius:12px;
padding:10px 12px; margin-top:10px;
}
#controls label { font-size:13px; color:#c9b8e8; }
input[type=range] { accent-color:#ff4fe1; width:170px; }
button {
background:#2e7d5b; color:#fff; border:none; border-radius:8px;
padding:7px 13px; font-weight:bold; cursor:pointer; font-size:13px;
}
button:hover { background:#3aa873; }
button.preset { background:#3d2a66; }
button.preset:hover { background:#553d8f; }
#speedVal { font-weight:bold; color:#7dffce; min-width:38px; }
#panel { flex: 0 1 300px; display:flex; flex-direction:column; gap:12px; }
.card { background:#1c1233; border:1px solid #3d2a66; border-radius:12px; padding:12px 14px; }
.card h2 { font-size:14px; letter-spacing:1px; color:#7dffce; margin-bottom:8px; text-transform:uppercase; }
.stat { display:flex; justify-content:space-between; font-size:13.5px; padding:3px 0; border-bottom:1px dashed #33255a; }
.stat:last-child { border-bottom:none; }
.stat b { color:#fff; }
.stat .hot { color:#ff7df0; }
#threat { text-align:center; font-weight:bold; font-size:15px; padding:9px; border-radius:9px; background:#12291f; color:#7dffce; transition:all .2s; }
#threat.lvl1 { background:#2a2a10; color:#ffe97d; box-shadow:0 0 14px rgba(255,230,80,.5); }
#threat.lvl2 { background:#3d0f2e; color:#ff7df0; box-shadow:0 0 20px rgba(255,47,214,.8); animation:blink .28s infinite alternate; }
#threat.event { background:#4d0018; color:#fff; box-shadow:0 0 28px rgba(255,0,80,.95); animation:blink .12s infinite alternate; }
@keyframes blink { from { filter:brightness(1);} to { filter:brightness(1.7);} }
#log { list-style:none; font-size:12.5px; max-height:196px; overflow:hidden; }
#log li { padding:4px 6px; border-radius:6px; margin-bottom:4px; background:#150c28; color:#c9b8e8; }
#log li.uni { background:#3d0f2e; color:#ffb7f6; border:1px solid #ff2fd6; }
#log li.boom { background:#4d0018; color:#fff; border:1px solid #ff3355; font-weight:bold; }
#log li.good { background:#12291f; color:#a9ffd9; }
.note { font-size:11.5px; color:#9d8ac4; line-height:1.5; }
.barOuter { height:10px; background:#150c28; border-radius:5px; overflow:hidden; margin-top:4px; }
#rateBar { height:100%; width:0%; background:linear-gradient(90deg,#00e5a0,#ff2fd6); transition:width .3s; }
#trueMark { font-size:11px; color:#9d8ac4; }
</style>
</head>
<body>
<header>
<h1>🦄 MOONBUCKS <span class="pink">NEON UNICORN FRAPPUCCINO</span> EVENT SIMULATOR ☕</h1>
<p><i>*definitely not Starbucks. Any resemblance to a coffee chain that would never do this is purely caffeinated.</i></p>
</header>
<div id="wrap">
<div id="stage">
<canvas id="cv" width="980" height="620"></canvas>
<div id="controls">
<button id="pauseBtn">⏸ Pause</button>
<label>Simulation speed <input type="range" id="speed" min="0.5" max="8" step="0.5" value="1"></label>
<span id="speedVal">1×</span>
<button class="preset" data-s="1">1×</button>
<button class="preset" data-s="2">2×</button>
<button class="preset" data-s="4">4×</button>
<button class="preset" data-s="8">8× 🚀</button>
<label style="margin-left:auto">Every drink rolls a fair d6. Unicorn on a 6. No pity timer. No mercy.</label>
</div>
</div>
<div id="panel">
<div id="threat">☮ REALITY STATUS: BORINGLY STABLE</div>
<div class="card">
<h2>📊 Live Statistics</h2>
<div class="stat"><span>Customers served</span><b id="sServed">0</b></div>
<div class="stat"><span>🦄 Neon Unicorns produced</span><b class="hot" id="sUni">0</b></div>
<div class="stat"><span>Observed unicorn rate</span><b id="sRate">—</b></div>
<div class="stat"><span>True rate</span><b>1 in 6 (16.67%)</b></div>
<div class="barOuter"><div id="rateBar"></div></div>
<div id="trueMark">▲ expected position of the bar: 16.67%</div>
<div class="stat"><span>Expected unicorns by now</span><b id="sExp">0.0</b></div>
<div class="stat"><span>Luck-o-meter (actual − expected)</span><b id="sLuck">0.0</b></div>
<div class="stat"><span>💥 Catastrophes</span><b class="hot" id="sBoom">0</b></div>
<div class="stat"><span>Customers currently inside</span><b id="sInside">0 / 8</b></div>
<div class="stat"><span>🦄 Unicorn holders inside NOW</span><b class="hot" id="sHolders">0</b></div>
<div class="stat"><span>Waiting in line outside</span><b id="sQueue">0</b></div>
<div class="stat"><span>Drinks per sim-minute</span><b id="sRateMin">0</b></div>
<div class="stat"><span>Sim time elapsed</span><b id="sTime">0:00</b></div>
<div class="stat"><span>Longest unicorn drought</span><b id="sDrought">0 drinks</b></div>
<div class="stat"><span>Current drought</span><b id="sCurDrought">0 drinks</b></div>
<div class="stat"><span>Customers who fled screaming</span><b id="sFled">0</b></div>
<div class="stat"><span>Sparkles emitted</span><b id="sSpark">0</b></div>
<div class="stat"><span>Emergency rebuilds</span><b id="sRebuild">0</b></div>
<div class="stat"><span>Barista sanity remaining</span><b id="sSanity">100%</b></div>
<div class="stat"><span>Neon radiation level</span><b id="sRad">0.0 mSv</b></div>
</div>
<div class="card">
<h2>🎲 Dice Log (rolled at the counter)</h2>
<ul id="log"></ul>
</div>
<div class="card note">
<b>How it works:</b> max 8 customers inside; the rest queue outside. Each finished
Frappuccino rolls one fair die — a 6 makes it a Neon Unicorn. The shop only reacts to how many
unicorn-holders are <i>inside at the same time</i>: 1 = something is slightly wrong,
2 = reality gets wobbly, 3 = 💥 <b>NEON UNICORN EVENT</b>. Nothing else can trigger it.
</div>
</div>
</div>
<script>
"use strict";
const cv = document.getElementById('cv'), ctx = cv.getContext('2d');
const W = 980, H = 620;
/* ---------- constants (the actual rules) ---------- */
const MAX_INSIDE = 8;
const ORDER_TIME = 2.0; // sim-seconds at the counter per drink
const SIP_TIME = 6.5; // sim-seconds holding the drink at a table (+ ~1s walk = ~7.5s total)
const UNICORN_FACE = 6; // d6 face that makes a Neon Unicorn
/* ---------- shop geometry ---------- */
const SHOP = { x:300, y:105, w:620, h:455 };
const DOOR = { x:322, y:478 };
const ORDER_SPOT = { x:648, y:308 };
const WAIT_SPOTS = [ {x:592,y:308},{x:536,y:308},{x:480,y:308},{x:424,y:308},{x:368,y:308} ];
const HOLD_SPOTS = [
{x:382,y:168},{x:472,y:162},{x:562,y:168},{x:640,y:180},
{x:382,y:452},{x:472,y:466},{x:562,y:466},{x:640,y:452}
];
/* ---------- state ---------- */
let customers = [], particles = [], notes = [];
let nextId = 1, simTime = 0, spawnTimer = 0, statTimer = 0;
let speed = 1, paused = false;
let eventActive = false, eventT = 0, collapse = 0, rebuildGlow = 0;
let barista = { busyWith:null, anim:0, x:716, y:308, panic:0 };
let stats = { served:0, uni:0, boom:0, fled:0, spark:0, rebuild:0,
drought:0, curDrought:0, sanity:100 };
const NAMES = ["Maya","Liam","Zoe","Noah","Ivy","Eli","Ruby","Omar","Nina","Kai",
"Luna","Finn","Ada","Hugo","Mira","Leo","Opal","Rex","Sana","Theo","Veda","Wren",
"Yuki","Zane","Cleo","Dario","Esme","Felix","Gia","Hank"];
const SHIRTS = ["#ff6b6b","#feca57","#48dbfb","#1dd1a1","#f368e0","#ff9f43","#54a0ff","#5f27cd","#c8d6e5","#fd79a8"];
function rand(a,b){ return a + Math.random()*(b-a); }
function pick(a){ return a[Math.floor(Math.random()*a.length)]; }
function fmtTime(s){ const m=Math.floor(s/60), ss=Math.floor(s%60); return m+":"+(ss<10?"0":"")+ss; }
function log(msg, cls){
const ul = document.getElementById('log');
const li = document.createElement('li');
li.textContent = msg; if(cls) li.className = cls;
ul.prepend(li);
while(ul.children.length > 8) ul.removeChild(ul.lastChild);
}
/* ---------- customers ---------- */
function spawnCustomer(){
customers.push({
id: nextId++, name: pick(NAMES), color: pick(SHIRTS),
x:-25, y: rand(560,595), tx:0, ty:0, speed: rand(78,102),
state:'QUEUE', inside:false, drink:null, roll:0,
orderT:0, sipT:0, spot:-1, phase: rand(0,6.28), panicX:0, panicY:0
});
}
function insideCount(){ return customers.filter(c=>c.inside).length; }
function unicornHolders(){
return customers.filter(c=>c.inside && c.drink==='unicorn' &&
(c.state==='TO_TABLE'||c.state==='HOLDING'||c.state==='LEAVING'||c.state==='ORDERING')).length;
}
function move(c, dt){
const dx = c.tx-c.x, dy = c.ty-c.y, d = Math.hypot(dx,dy);
if(d < 4) return true;
const step = Math.min(d, c.speed*dt);
c.x += dx/d*step; c.y += dy/d*step;
return d < 6;
}
function queueSlot(i){
if(i < 9) return { x: 268 - i*29, y: 512 };
return { x: 268 - (i-9)*29, y: 552 };
}
function freeHoldSpot(){
const used = new Set(customers.map(c=>c.spot).filter(s=>s>=0));
for(let i=0;i<HOLD_SPOTS.length;i++) if(!used.has(i)) return i;
return -1;
}
/* ---------- particles ---------- */
function burst(x,y,n,cols,spd,life,size){
for(let i=0;i<n;i++){
const a = rand(0,6.28), s = rand(spd*0.3,spd);
particles.push({x,y,vx:Math.cos(a)*s,vy:Math.sin(a)*s-40,
life:rand(life*0.4,life), maxLife:life, size:rand(size*0.5,size),
col:pick(cols), grav:120, shape:'rect'});
stats.spark++;
}
}
function sparkle(x,y){
particles.push({x:x+rand(-8,8), y:y+rand(-14,4), vx:rand(-14,14), vy:rand(-38,-14),
life:rand(.4,1), maxLife:1, size:rand(2,4.5),
col:pick(['#ff7df0','#7dffce','#ffe97d','#7dd7ff','#ffffff']), grav:-20, shape:'star'});
stats.spark++;
}
/* ---------- the one and only dice roll ---------- */
function finishOrder(c){
const roll = Math.floor(Math.random()*6) + 1; // fair d6, rolled per drink, at the counter
c.roll = roll;
stats.served++;
const isUni = (roll === UNICORN_FACE);
c.drink = isUni ? 'unicorn' : 'normal';
if(isUni){
stats.uni++; stats.curDrought = 0;
log("🎲 "+c.name+" rolled a "+roll+" — 🦄 NEON UNICORN FRAPPUCCINO!!", "uni");
burst(c.x, c.y-16, 22, ['#ff7df0','#7dffce','#ffe97d','#7dd7ff'], 130, .9, 5);
} else {
stats.curDrought++;
if(stats.curDrought > stats.drought) stats.drought = stats.curDrought;
log("🎲 "+c.name+" rolled a "+roll+" — regular Frappuccino. Reality holds.", "");
}
barista.busyWith = null;
const s = freeHoldSpot();
c.spot = s;
if(s >= 0){ c.tx = HOLD_SPOTS[s].x + 20; c.ty = HOLD_SPOTS[s].y + 6; c.state='TO_TABLE'; }
else { c.tx = 640; c.ty = 400; c.state='TO_TABLE'; }
checkCatastrophe();
}
function checkCatastrophe(){
if(eventActive) return;
if(unicornHolders() >= 3){
eventActive = true; eventT = 0;
stats.boom++; stats.sanity = Math.max(3, stats.sanity - 34);
log("💥💥💥 THREE NEON UNICORNS IN THE ROOM. NEON UNICORN EVENT!!!", "boom");
burst(SHOP.x+SHOP.w/2, SHOP.y+180, 150, ['#ff7df0','#7dffce','#ffe97d','#ff3355','#ffffff'], 340, 1.6, 7);
customers.forEach(c=>{
if(c.inside){
c.state='FLEE'; c.inside=false; c.spot=-1; stats.fled++;
c.tx = -40; c.ty = rand(480,600);
} else if(c.state==='QUEUE'){
c.state='PANIC'; c.panicX = c.x + rand(-46,46); c.panicY = c.y + rand(-24,24);
}
});
barista.busyWith = null; barista.panic = 1;
}
}
/* ---------- update ---------- */
function update(dt){
simTime += dt;
/* event timeline (presentation only — the trigger was purely the count) */
if(eventActive){
eventT += dt;
if(eventT < 1.2) collapse = Math.min(1, collapse + dt*1.4);
else if(eventT > 7 && eventT < 9.6) collapse = Math.max(0, collapse - dt*0.55);
if(eventT > 1.2 && eventT < 7 && Math.random() < 0.5)
burst(rand(SHOP.x+120,SHOP.x+520), rand(180,330), 3, ['#ff7df0','#7dffce','#ffe97d','#7dd7ff'], 90, 1.1, 5);
if(eventT >= 9.6){
eventActive = false; collapse = 0; rebuildGlow = 1.4; stats.rebuild++;
log("🔨 Shop rebuilt overnight (somehow). The promotion continues. Nobody learned anything.", "good");
customers.forEach(c=>{ if(c.state==='PANIC') c.state='QUEUE'; });
barista.panic = 0;
}
}
if(rebuildGlow > 0) rebuildGlow -= dt;
/* spawning */
spawnTimer -= dt;
if(!eventActive && spawnTimer <= 0 && customers.length < 26){
spawnCustomer(); spawnTimer = rand(0.55, 1.25);
}
/* outside queue targets */
const q = customers.filter(c=>c.state==='QUEUE');
q.forEach((c,i)=>{ const s = queueSlot(i); c.tx=s.x; c.ty=s.y; });
/* admission: front of queue enters if there is room */
if(!eventActive && q.length && insideCount() < MAX_INSIDE){
const front = q[0];
if(Math.hypot(front.x-268, front.y-512) < 14){
front.state='ENTERING'; front.inside = true;
front.tx = DOOR.x + 34; front.ty = DOOR.y;
}
}
/* inside line targets */
const line = customers.filter(c=>c.state==='IN_LINE' || c.state==='ORDERING');
line.forEach((c,i)=>{
if(c.state==='ORDERING') return;
const s = i===0 ? ORDER_SPOT : WAIT_SPOTS[Math.min(i-1, WAIT_SPOTS.length-1)];
c.tx = s.x; c.ty = s.y;
});
for(const c of customers){
c.phase += dt*4;
switch(c.state){
case 'QUEUE': case 'PANIC':
if(c.state==='PANIC'){ c.tx=c.panicX; c.ty=c.panicY; }
move(c, dt); break;
case 'ENTERING':
if(move(c,dt)) c.state='IN_LINE'; break;
case 'IN_LINE': {
move(c, dt);
const idx = line.indexOf(c);
if(idx===0 && Math.hypot(c.x-ORDER_SPOT.x, c.y-ORDER_SPOT.y) < 8 && !barista.busyWith && !eventActive){
c.state='ORDERING'; c.orderT = ORDER_TIME; barista.busyWith = c.id;
}
break; }
case 'ORDERING':
barista.anim += dt;
c.orderT -= dt;
if(c.orderT <= 0) finishOrder(c);
break;
case 'TO_TABLE':
if(c.drink==='unicorn' && Math.random()<0.35) sparkle(c.x, c.y-14);
if(move(c,dt)){ c.state='HOLDING'; c.sipT = SIP_TIME; }
break;
case 'HOLDING':
if(c.drink==='unicorn' && Math.random()<0.22) sparkle(c.x, c.y-14);
c.sipT -= dt;
if(c.sipT <= 0){ c.state='LEAVING'; c.spot=-1; c.tx=DOOR.x+30; c.ty=DOOR.y; }
break;
case 'LEAVING':
if(move(c,dt)){ c.state='EXITING'; c.tx=-40; c.ty=rand(520,590); }
if(c.x < DOOR.x + 6) c.inside = false;
break;
case 'EXITING': case 'FLEE':
c.speed = c.state==='FLEE' ? 210 : 95;
move(c, dt); break;
}
}
customers = customers.filter(c => !( (c.state==='EXITING'||c.state==='FLEE') && c.x < -30 ));
/* particles */
for(const p of particles){
p.life -= dt; p.vy += p.grav*dt; p.x += p.vx*dt; p.y += p.vy*dt;
}
particles = particles.filter(p=>p.life>0);
for(let i=notes.length-1;i>=0;i--){ notes[i].life-=dt; notes[i].y-=26*dt; if(notes[i].life<=0) notes.splice(i,1); }
if(eventActive && eventT>1.2 && eventT<7 && Math.random()<0.3)
notes.push({x:SHOP.x+310+rand(-130,130), y:rand(150,240), life:1.4, txt:pick(['♪','♫','🦄','✨','NEON!','♪'])});
}
/* ---------- drawing ---------- */
function roundRect(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 drawShop(level, t){
const cx = SHOP.x, cy = SHOP.y, cw = SHOP.w, ch = SHOP.h;
const rubble = collapse;
/* floor */
ctx.fillStyle = '#241a3f'; ctx.fillRect(cx, cy, cw, ch);
ctx.fillStyle = '#2c2050';
for(let i=0;i<8;i++) for(let j=0;j<6;j++) if((i+j)%2===0) ctx.fillRect(cx+i*cw/8, cy+j*ch/6, cw/8, ch/6);
if(level===1){ ctx.fillStyle='rgba(255,47,214,0.06)'; ctx.fillRect(cx,cy,cw,ch); }
if(level>=2){ ctx.fillStyle='hsla('+((t*160)%360)+',100%,60%,0.13)'; ctx.fillRect(cx,cy,cw,ch); }
/* tables */
for(const s of HOLD_SPOTS){
ctx.fillStyle = '#4a3577'; ctx.beginPath(); ctx.arc(s.x, s.y, 17, 0, 6.29); ctx.fill();
ctx.fillStyle = '#5d4396'; ctx.beginPath(); ctx.arc(s.x, s.y, 11, 0, 6.29); ctx.fill();
}
/* counter */
ctx.fillStyle = '#173f2c'; roundRect(672, 208, 218, 208, 10); ctx.fill();
ctx.fillStyle = '#1f5c3e'; roundRect(672, 208, 218, 26, 10); ctx.fill();
ctx.fillStyle = '#7dffce'; ctx.font = 'bold 12px Trebuchet MS'; ctx.textAlign='center';
ctx.fillText('— ORDER HERE → REGRET LATER —', 781, 226);
/* menu board */
ctx.fillStyle = '#0f2a1c'; roundRect(690, 128, 182, 62, 6); ctx.fill();
ctx.fillStyle = '#a9ffd9'; ctx.font = '11px Trebuchet MS'; ctx.textAlign='left';
ctx.fillText('MENU', 700, 146);
ctx.fillStyle = level>=1 ? '#ff7df0' : '#d9ffe f'.replace(' ','');
ctx.fillText('Frappuccino ........ $5', 700, 162);
ctx.fillText(level>=1 ? '🦄 ??? ............ $5' : 'Unicorn?? ....... n/a', 700, 177);
/* walls / roof, collapsing */
const drop = rubble * 120, tilt = rubble * 0.22;
ctx.save();
ctx.translate(cx+cw/2, cy+40); ctx.rotate(tilt * Math.sin(t*2)); ctx.translate(-(cx+cw/2), -(cy+40));
ctx.fillStyle = rubble>0.5 ? '#5a4a3a' : '#e8f5ee';
ctx.fillRect(cx, cy+drop*0.4, cw, 16);
ctx.fillStyle = '#14663c';
ctx.beginPath();
ctx.moveTo(cx-14, cy+drop*0.4); ctx.lineTo(cx+cw/2, cy-58+drop); ctx.lineTo(cx+cw+14, cy+drop*0.4); ctx.closePath(); ctx.fill();
/* sign */
const flicker = level===1 ? (Math.sin(t*23)>0.7 ? 0.35 : 1) : 1;
ctx.globalAlpha = (1-rubble) * flicker;
ctx.fillStyle = '#0b3d24'; roundRect(cx+150, cy-46+drop, 320, 42, 10); ctx.fill();
ctx.fillStyle = '#7dffce'; ctx.font='bold 24px Trebuchet MS'; ctx.textAlign='center';
ctx.shadowColor = level>=1 ? '#ff2fd6' : '#00c853'; ctx.shadowBlur = level>=1 ? 22 : 10;
ctx.fillText(level>=2 && Math.sin(t*17)>0 ? 'MOON🦄UCKS' : 'MOONBUCKS', cx+cw/2, cy-17+drop);
ctx.shadowBlur = 0; ctx.globalAlpha = 1;
ctx.restore();
/* rubble pile */
if(rubble > 0.05){
ctx.fillStyle = '#6b5a49';
for(let i=0;i<16;i++){
const rx = cx + 30 + (i*37)%(cw-60), ry = cy+ch-16 - ((i*23)%26) * rubble;
ctx.beginPath(); ctx.moveTo(rx, ry); ctx.lineTo(rx+22, ry-14*rubble); ctx.lineTo(rx+44, ry); ctx.closePath(); ctx.fill();
}
ctx.fillStyle = '#ff7df0'; ctx.font='bold 15px Trebuchet MS'; ctx.textAlign='center';
if(eventActive && eventT < 7) ctx.fillText('⚠ FORMERLY A COFFEE SHOP ⚠', cx+cw/2, cy+ch-34);
}
if(rebuildGlow > 0){
ctx.fillStyle = 'rgba(125,255,206,'+(rebuildGlow*0.25)+')'; ctx.fillRect(cx,cy,cw,ch);
ctx.fillStyle = '#7dffce'; ctx.font='bold 18px Trebuchet MS'; ctx.textAlign='center';
ctx.fillText('✨ REBUILDING… PLEASE PRETEND THIS IS NORMAL ✨', cx+cw/2, cy+70);
}
/* door */
ctx.fillStyle = '#0b3d24'; ctx.fillRect(DOOR.x-14, DOOR.y-34, 30, 74);
ctx.fillStyle = '#9be7ff'; ctx.fillRect(DOOR.x-9, DOOR.y-28, 20, 30);
/* windows with wrongness */
for(let i=0;i<3;i++){
const wx = cx+52+i*86, wy = cy+52;
ctx.fillStyle = level>=2 ? 'hsl('+((t*240+i*60)%360)+',100%,70%)' : '#9be7ff';
ctx.globalAlpha = 0.75; ctx.fillRect(wx, wy, 62, 74); ctx.globalAlpha = 1;
if(level===1 && i===1){ ctx.fillStyle='#201335'; ctx.font='16px Trebuchet MS'; ctx.textAlign='center'; ctx.fillText('🦄?', wx+31, wy+44); }
}
/* barista */
if(!eventActive || eventT > 8){
const bx = barista.x, by = barista.y + Math.sin(t*3)*2;
ctx.fillStyle = '#173f2c'; ctx.beginPath(); ctx.arc(bx, by-20, 13, 0, 6.29); ctx.fill(); /* cap */
ctx.fillStyle = '#ffd9b3'; ctx.beginPath(); ctx.arc(bx, by-14, 11, 0, 6.29); ctx.fill();
ctx.fillStyle = '#1f5c3e'; roundRect(bx-13, by-4, 26, 26, 6); ctx.fill();
if(barista.busyWith){
const shake = Math.sin(barista.anim*30)*3;
ctx.strokeStyle = '#ffd9b3'; ctx.lineWidth=4; ctx.beginPath();
ctx.moveTo(bx+10, by+4); ctx.lineTo(bx+24+shake, by-8); ctx.stroke();
ctx.fillStyle='#fff'; ctx.fillRect(bx+22+shake, by-18, 9, 12);
}
if(stats.sanity < 55){ ctx.font='13px Trebuchet MS'; ctx.textAlign='center'; ctx.fillText('😵', bx, by-36); }
}
}
function drawCustomer(c, t, level){
const bob = (c.state==='QUEUE'||c.state==='PANIC') ? Math.sin(c.phase)*1.6 : Math.sin(c.phase*0.7)*1;
const panicShake = c.state==='PANIC' ? rand(-2.5,2.5) : 0;
const x = c.x + panicShake, y = c.y + bob;
ctx.fillStyle = 'rgba(0,0,0,.3)'; ctx.beginPath(); ctx.ellipse(c.x, c.y+13, 11, 4, 0, 0, 6.29); ctx.fill();
ctx.fillStyle = c.color; ctx.beginPath(); ctx.arc(x, y, 11, 0, 6.29); ctx.fill();
ctx.fillStyle = '#ffe0bd'; ctx.beginPath(); ctx.arc(x, y-3, 6.5, 0, 6.29); ctx.fill();
/* drink */
if(c.drink){
const dx = x+13, dy = y+2;
if(c.drink==='unicorn'){
const hue = (t*220 + c.id*47) % 360;
ctx.shadowColor = 'hsl('+hue+',100%,65%)'; ctx.shadowBlur = 16;
ctx.fillStyle = 'hsl('+hue+',100%,72%)';
ctx.fillRect(dx-4, dy-8, 9, 13);
ctx.shadowBlur = 0;
ctx.fillStyle = '#fff'; ctx.fillRect(dx-1, dy-13, 2.5, 6);
ctx.font='11px Trebuchet MS'; ctx.textAlign='center'; ctx.fillText('🦄', dx+1, dy-15);
} else {
ctx.fillStyle = '#e8f5ee'; ctx.fillRect(dx-4, dy-7, 8, 12);
ctx.fillStyle = '#7a4a21'; ctx.fillRect(dx-4, dy-7, 8, 5);
}
}
if(c.state==='ORDERING'){
ctx.fillStyle='#fff'; ctx.font='11px Trebuchet MS'; ctx.textAlign='center';
ctx.fillText('one Frappuccino pls…', x, y-30);
}
if(c.state==='FLEE'){ ctx.font='13px Trebuchet MS'; ctx.textAlign='center'; ctx.fillText('😱', x, y-20); }
}
function drawGiantUnicorn(t){
const cx = SHOP.x + 310, baseY = 400;
const bounce = Math.abs(Math.sin(t*5.2)) * -34;
const y = baseY + bounce, s = 1.55;
ctx.save(); ctx.translate(cx, y); ctx.scale(s, s);
const hue = (t*160) % 360;
ctx.shadowColor = 'hsl('+hue+',100%,65%)'; ctx.shadowBlur = 30;
/* legs */
ctx.strokeStyle = '#fff'; ctx.lineWidth = 7; ctx.lineCap='round';
for(let i=0;i<4;i++){
const lx = -52 + i*34, sw = Math.sin(t*10 + i*1.7) * 20;
ctx.beginPath(); ctx.moveTo(lx, 28); ctx.lineTo(lx+sw, 74); ctx.stroke();
}
/* body */
ctx.fillStyle = '#ffffff'; ctx.beginPath(); ctx.ellipse(0, 0, 78, 42, 0, 0, 6.29); ctx.fill();
/* tail */
ctx.strokeStyle = 'hsl('+((hue+120)%360)+',100%,65%)'; ctx.lineWidth=9;
ctx.beginPath(); ctx.moveTo(-76,-6); ctx.quadraticCurveTo(-108, Math.sin(t*7)*22, -96, 38); ctx.stroke();
/* neck + head */
ctx.fillStyle = '#fff'; ctx.beginPath(); ctx.ellipse(66, -34, 30, 24, -0.5, 0, 6.29); ctx.fill();
ctx.beginPath(); ctx.ellipse(92, -22, 17, 11, -0.3, 0, 6.29); ctx.fill();
/* mane */
for(let i=0;i<5;i++){
ctx.fillStyle = 'hsl('+((hue+i*55)%360)+',100%,65%)';
ctx.beginPath();
ctx.moveTo(38-i*7, -58+i*13); ctx.lineTo(58-i*7, -44+i*13); ctx.lineTo(40-i*7, -34+i*13); ctx.closePath(); ctx.fill();
}
/* horn */
const grad = ctx.createLinearGradient(58,-92,74,-58);
grad.addColorStop(0,'#ffe97d'); grad.addColorStop(.5,'#ff7df0'); grad.addColorStop(1,'#7dd7ff');
ctx.fillStyle = grad; ctx.beginPath();
ctx.moveTo(56,-56); ctx.lineTo(66,-96); ctx.lineTo(76,-54); ctx.closePath(); ctx.fill();
/* eye + laser sparkle */
ctx.fillStyle = '#201335'; ctx.beginPath(); ctx.arc(72,-38,4,0,6.29); ctx.fill();
ctx.font='15px Trebuchet MS'; ctx.textAlign='center'; ctx.fillText('✨', 96, -48 + Math.sin(t*9)*4);
ctx.restore();
ctx.shadowBlur = 0;
ctx.fillStyle = '#ff7df0'; ctx.font='bold 26px Trebuchet MS'; ctx.textAlign='center';
ctx.shadowColor='#ff2fd6'; ctx.shadowBlur=18;
ctx.fillText('🦄 THE NEON UNICORN HAS ARRIVED 🦄', cx, 128 + Math.sin(t*5.2)*5);
ctx.font='bold 14px Trebuchet MS'; ctx.fillStyle='#7dffce';
ctx.fillText('it is dancing. nothing can stop it. the drinks are free now. forever.', cx, 152 + Math.sin(t*5.2)*5);
ctx.shadowBlur = 0;
}
function draw(){
const level = eventActive ? 3 : unicornHolders();
const t = simTime;
ctx.clearRect(0,0,W,H);
/* sky / street */
const sky = ctx.createLinearGradient(0,0,0,H);
sky.addColorStop(0, level>=2 ? '#2b0730' : '#17102a'); sky.addColorStop(1,'#0d2b1e');
ctx.fillStyle = sky; ctx.fillRect(0,0,W,H);
ctx.fillStyle = '#3a3a4a'; ctx.fillRect(0, 528, W, 92); /* sidewalk/road */
ctx.fillStyle = '#55556a'; ctx.fillRect(0, 528, W, 6);
ctx.fillStyle = '#8f8fa8'; ctx.font='12px Trebuchet MS'; ctx.textAlign='left';
ctx.fillText('← the line (it never truly ends)', 14, 606);
ctx.fillText('☕ NOW SERVING: questionable decisions', 640, 606);
/* screen shake */
ctx.save();
let sh = 0;
if(level===2) sh = 3.5; if(eventActive && eventT<1.4) sh = 15; else if(eventActive) sh = 4;
if(sh) ctx.translate(rand(-sh,sh), rand(-sh,sh));
if(level===2) ctx.transform(1, Math.sin(t*21)*0.006, Math.sin(t*17)*0.006, 1, 0, 0);
if(level===2 && Math.sin(t*13)>0.2){ ctx.fillStyle='rgba(255,255,255,0.10)'; ctx.fillRect(0,0,W,H); }
drawShop(level, t);
if(eventActive && eventT>=1.0 && eventT<7.2) drawGiantUnicorn(t);
/* queue + customers (sorted by y for fake depth) */
const sorted = [...customers].sort((a,b)=>a.y-b.y);
for(const c of sorted) drawCustomer(c, t, level);
/* particles */
for(const p of particles){
ctx.globalAlpha = Math.max(0, p.life/p.maxLife);
ctx.fillStyle = p.col;
if(p.shape==='star'){ ctx.font = p.size*3+'px Trebuchet MS'; ctx.textAlign='center'; ctx.fillText('✦', p.x, p.y); }
else ctx.fillRect(p.x, p.y, p.size, p.size);
}
ctx.globalAlpha = 1;
for(const n of notes){
ctx.globalAlpha = Math.min(1, n.life); ctx.fillStyle='#ff7df0';
ctx.font='20px Trebuchet MS'; ctx.textAlign='center'; ctx.fillText(n.txt, n.x, n.y);
}
ctx.globalAlpha = 1;
/* level banners */
ctx.textAlign='center';
if(level===1 && !eventActive){
ctx.fillStyle='rgba(255,125,240,.9)'; ctx.font='bold 15px Trebuchet MS';
ctx.fillText('…something in here is glowing. probably fine.', SHOP.x+SHOP.w/2, 92);
}
if(level===2 && !eventActive){
ctx.fillStyle = Math.sin(t*16)>0 ? '#ff7df0' : '#ffe97d';
ctx.font='bold 19px Trebuchet MS'; ctx.shadowColor='#ff2fd6'; ctx.shadowBlur=14;
ctx.fillText('⚠ REALITY INTEGRITY FAILING — 2 NEON UNICORNS ⚠', SHOP.x+SHOP.w/2, 92);
ctx.shadowBlur=0;
}
ctx.restore();
}
/* ---------- stats panel ---------- */
const $ = id => document.getElementById(id);
function refreshStats(){
const holders = unicornHolders();
$('sServed').textContent = stats.served;
$('sUni').textContent = stats.uni;
const rate = stats.served ? (stats.uni/stats.served*100) : 0;
$('sRate').textContent = stats.served ? rate.toFixed(2)+'% (1 in '+(stats.uni? (stats.served/stats.uni).toFixed(1):'∞')+')' : '—';
$('rateBar').style.width = Math.min(100, rate*2.4)+'%';
const exp = stats.served/6;
$('sExp').textContent = exp.toFixed(1);
const luck = stats.uni - exp;
$('sLuck').textContent = (luck>=0?'+':'') + luck.toFixed(1) + (luck>2?' 🍀 suspicious':luck<-2?' 🧊 cursed':'');
$('sBoom').textContent = stats.boom;
$('sInside').textContent = insideCount()+' / '+MAX_INSIDE;
$('sHolders').textContent = holders;
$('sQueue').textContent = customers.filter(c=>c.state==='QUEUE').length;
$('sRateMin').textContent = simTime>5 ? (stats.served/(simTime/60)).toFixed(1) : '0';
$('sTime').textContent = fmtTime(simTime);
$('sDrought').textContent = stats.drought+' drinks';
$('sCurDrought').textContent = stats.curDrought+' drinks';
$('sFled').textContent = stats.fled;
$('sSpark').textContent = stats.spark.toLocaleString();
$('sRebuild').textContent = stats.rebuild;
$('sSanity').textContent = stats.sanity+'%';
$('sRad').textContent = (holders*13.7 + (eventActive?999:0)).toFixed(1)+' mSv';
const th = $('threat');
if(eventActive){ th.className='event'; th.id='threat'; th.textContent='💥🦄 NEON UNICORN EVENT IN PROGRESS 🦄💥'; }
else if(holders>=2){ th.className='lvl2'; th.textContent='⚠ THREAT LEVEL: WOBBLY (2 unicorns — one more and it\'s over)'; }
else if(holders===1){ th.className='lvl1'; th.textContent='✨ THREAT LEVEL: SLIGHTLY WRONG (1 unicorn inside)'; }
else { th.className=''; th.textContent='☮ REALITY STATUS: BORINGLY STABLE'; }
}
/* ---------- controls ---------- */
const speedInput = $('speed');
function setSpeed(v){
speed = parseFloat(v); speedInput.value = speed;
$('speedVal').textContent = speed+'×';
}
speedInput.addEventListener('input', e=>setSpeed(e.target.value));
document.querySelectorAll('.preset').forEach(b=>b.addEventListener('click', ()=>setSpeed(b.dataset.s)));
$('pauseBtn').addEventListener('click', ()=>{
paused = !paused;
$('pauseBtn').textContent = paused ? '▶ Resume' : '⏸ Pause';
});
/* ---------- main loop ---------- */
let last = performance.now();
function frame(now){
let dtReal = Math.min(0.05, (now-last)/1000); last = now;
if(!paused){
const dt = dtReal * speed;
update(dt);
statTimer -= dtReal;
if(statTimer <= 0){ refreshStats(); statTimer = 0.15; }
}
draw();
requestAnimationFrame(frame);
}
log("☕ Moonbucks is open. The Neon Unicorn Frappuccino promotion begins. What could go wrong.", "good");
for(let i=0;i<5;i++) spawnCustomer();
refreshStats();
requestAnimationFrame(frame);
</script>
</body>
</html>
In the first few minutes you'll see customers spawn on the left, shuffle along the outside line (max 8 let inside at once), queue at the counter, get served by the increasingly traumatised barista, then carry their drink to a table for ~6.5 seconds before leaving. Every finished drink visibly rolls a d6 in the dice log — a 6 produces a glowing, hue-cycling Neon Unicorn with sparkles. One unicorn holder inside makes the sign flicker, tints the shop pink and puts a suspicious "🦄?" in the window; two makes the whole building shake, strobes the lights, warps the colours and tanks the "reality integrity" banner; the instant a third unicorn holder is inside, the shop explodes into particles, collapses into a rubble pile, everyone inside flees screaming off-screen, and a giant procedurally-drawn neon unicorn dances on the ruins amid confetti and floating musical notes for a few seconds before the shop sparkle-rebuilds and business resumes as if nothing happened. At 1× you should get a catastrophe roughly every couple of minutes; the speed slider (0.5×–8×, plus presets and pause) lets you fast-forward through the quiet stretches.
Things I tuned, special-cased or nudged — honestly declared: the dice themselves are not nudged at all (one fair Math.random() d6 per drink, unicorn only on a 6, no pity timer, no quota, no scheduled event — the catastrophe check is literally just "are ≥3 unicorn-holders inside right now?"). What I did tune is the stage around the dice: the 2.0s service time and ~7.5s effective holding time were chosen specifically so unicorns overlap often enough to make triples a minutes-scale event rather than an hourly one, and the 8-customer cap keeps the room crowded enough for that overlap to matter. The event animation is scripted in duration (explosion → ~6s of dancing → rebuild) once triggered, but its timing and occurrence never are. Customers inside during a catastrophe are deleted via fleeing rather than finishing their drinks, which slightly truncates some unicorn lifetimes — that only ever delays the next event, never causes one.
What it does badly: the inside line is a polite single file with fixed spots, so customers occasionally overlap or take geometrically stupid paths through tables; fleeing customers run through walls; the observed-rate bar is scaled for drama rather than accuracy (the numbers beside it are exact, though); at 8× the dice log becomes an unreadable blur and particle counts can chug on weak machines; and the barista's sanity metric is pure theatre — it only ever goes down, which, to be fair, is also how the job works.