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 chosen timings: counter service takes 2.5–4.5s (uniform), drink-hold inside the shop is 4–8s, walk speeds are ~80px/s, and the shop fits 8 customers. The counter is the bottleneck, yielding ~18–20 customers/minute. Each customer rolls 1d6 for a Neon Unicorn (16.67%). Unicorns stay inside for ~6–10s (hold + exit walk), so the steady-state mean unicorns inside is λ × T ≈ (19/6 per minute) × (8/60 min) ≈ 0.42. The number of unicorns inside approximates a Poisson(0.42) distribution. P(≥3) = 1 − P(0) − P(1) − P(2) ≈ 1 − 0.657 − 0.276 − 0.058 ≈ 0.009. Catastrophes occur when a 3rd unicorn arrives while 2 are already present: arrival rate of unicorns ≈ 3.17/min, fraction of time with exactly 2 inside ≈ 0.058, so catastrophe rate ≈ 3.17 × 0.058 ≈ 0.18/min — one catastrophe every 5–6 minutes of simulated time. At 10× speed, expect a disaster every 30–40 real seconds. No nudging; the dice decide.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Neon Unicorn Frappuccino Simulator</title>
<style>
:root{
--bg:#0d0d12; --fg:#e8e8f0; --muted:#6a6a7a; --accent:#00ffd5; --warn:#ffcc00; --danger:#ff2a6d;
--panel:#16161f; --border:#2a2a3a; --glass:rgba(255,255,255,0.04);
}
*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%;background:var(--bg);color:var(--fg);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;overflow:hidden}
#wrap{display:grid;grid-template-rows:1fr auto;height:100%}
#canvas{display:block;background:#08080c;touch-action:none}
#hud{display:grid;grid-template-columns:repeat(4,1fr) auto;gap:8px;padding:10px 14px;background:var(--panel);border-top:1px solid var(--border);font-size:11px;line-height:1.4;min-height:72px}
.stat{background:var(--glass);border:1px solid var(--border);border-radius:6px;padding:6px 8px;display:flex;flex-direction:column;min-width:0}
.stat .label{color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.5px}
.stat .value{font-size:14px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.stat.good .value{color:var(--accent)}
.stat.warn .value{color:var(--warn)}
.stat.bad .value{color:var(--danger)}
.stat.unicorn .value{color:#ff00ff;text-shadow:0 0 8px #ff00ff}
#speedWrap{display:flex;align-items:center;gap:8px;padding:0 4px}
#speedLabel{min-width:48px;text-align:right;color:var(--muted);font-size:11px}
#speed{flex:1;max-width:220px;accent-color:var(--accent);height:6px}
#pauseBtn{width:44px;height:30px;border:1px solid var(--border);background:var(--glass);color:var(--fg);border-radius:6px;cursor:pointer;font-size:11px;transition:all .15s}
#pauseBtn:hover{background:var(--accent);color:#000;border-color:var(--accent)}
#pauseBtn:active{transform:scale(.96)}
#eventBanner{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;pointer-events:none;z-index:10;opacity:0;transition:opacity .3s}
#eventBanner.show{opacity:1}
#eventBanner span{background:rgba(0,0,0,0.9);border:3px solid #ff00ff;padding:24px 48px;border-radius:12px;font-size:28px;font-weight:800;letter-spacing:2px;color:#ff00ff;text-shadow:0 0 30px #ff00ff,0 0 60px #ff00ff;animation:bannerPulse .5s ease-in-out infinite alternate;transform:scale(0.8);transition:transform .3s cubic-bezier(.2,.8,.2,1)}
#eventBanner.show span{transform:scale(1)}
@keyframes bannerPulse{from{text-shadow:0 0 20px #ff00ff,0 0 40px #ff00ff}to{text-shadow:0 0 40px #ff00ff,0 0 80px #ff00ff,0 0 120px #ff00ff}}
@media (max-width:700px){
#hud{grid-template-columns:repeat(2,1fr);font-size:10px}
#speedWrap{grid-column:span 2}
#eventBanner span{font-size:18px;padding:16px 24px}
}
</style>
</head>
<body>
<div id="wrap">
<canvas id="canvas"></canvas>
<div id="hud">
<div class="stat"><div class="label">Customers Served</div><div class="value" id="statServed">0</div></div>
<div class="stat"><div class="label">Neon Unicorns Produced</div><div class="value unicorn" id="statUnicorns">0</div></div>
<div class="stat"><div class="label">Observed Unicorn Rate</div><div class="value" id="statRate">0.00%</div></div>
<div class="stat"><div class="label">True Rate (1/6)</div><div class="value muted">16.67%</div></div>
<div class="stat"><div class="label">Catastrophes</div><div class="value warn" id="statCatas">0</div></div>
<div class="stat"><div class="label">Current Unicorns Inside</div><div class="value unicorn" id="statInside">0</div></div>
<div class="stat"><div class="label">Peak Unicorns Inside</div><div class="value unicorn" id="statPeak">0</div></div>
<div class="stat"><div class="label">Time Since Last Catastrophe</div><div class="value" id="statSince">0.0s</div></div>
<div class="stat"><div class="label">Simulation Time</div><div class="value" id="statTime">0.0s</div></div>
<div class="stat"><div class="label">Queue Length</div><div class="value" id="statQueue">0</div></div>
<div class="stat"><div class="label">Inside Count</div><div class="value" id="statInsideCount">0/8</div></div>
<div id="speedWrap">
<span id="speedLabel">1.0×</span>
<input type="range" id="speed" min="0.1" max="20" step="0.1" value="1">
<button id="pauseBtn" title="Pause/Resume">⏸</button>
</div>
</div>
</div>
<div id="eventBanner"><span>NEON UNICORN EVENT</span></div>
<script>
/* ============ CONFIG ============ */
const CFG = {
canvasId: 'canvas',
maxInside: 8,
unicornChance: 1/6,
// timings (seconds)
walkSpeed: 80, // px/s
serveTimeMin: 2.5,
serveTimeMax: 4.5,
holdTimeMin: 4,
holdTimeMax: 8,
// layout (relative to canvas)
entranceY: 1, // bottom (1.0) to top (0.0)
counterY: 0.12,
pickupY: 0.38,
holdAreaY: 0.55,
exitY: 0.95,
queueStartX: 0.5,
queueSpacing: 28,
// visual
customerRadius: 6,
drinkRadius: 4,
neonColors: [
'#ff00ff','#00ffff','#ffff00','#ff4400','#44ff00','#ff0088','#8800ff','#00ff88'
],
// catastrophe
catastropheDuration: 8, // seconds
rebuildDuration: 3,
// simulation
maxSpeed: 20,
spawnIntervalBase: 1.2, // seconds between spawn attempts when queue not full
maxQueue: 30
};
/* ============ UTIL ============ */
const $ = (sel) => document.querySelector(sel);
const rand = (min,max) => min + Math.random()*(max-min);
const randInt = (min,max) => Math.floor(rand(min,max+1));
const clamp = (v,min,max) => Math.max(min,Math.min(max,v));
const lerp = (a,b,t) => a + (b-a)*t;
const hsl = (h,s,l) => `hsl(${h},${s}%,${l}%)`;
const pick = (arr) => arr[randInt(0,arr.length-1)];
/* ============ RNG (seeded for reproducibility if needed) ============ */
let rngSeed = Date.now() >>> 0;
function rng() {
rngSeed = (rngSeed * 1664525 + 1013904223) >>> 0;
return rngSeed / 4294967296;
}
function randSeeded(min,max){ return min + rng()*(max-min); }
function randIntSeeded(min,max){ return Math.floor(randSeeded(min,max+1)); }
function rollUnicorn(){ return rng() < CFG.unicornChance; }
/* ============ VECTOR ============ */
class Vec {
constructor(x=0,y=0){ this.x=x; this.y=y; }
set(x,y){ this.x=x; this.y=y; return this; }
copy(){ return new Vec(this.x,this.y); }
add(v){ this.x+=v.x; this.y+=v.y; return this; }
sub(v){ this.x-=v.x; this.y-=v.y; return this; }
mul(s){ this.x*=s; this.y*=s; return this; }
div(s){ this.x/=s; this.y/=s; return this; }
len(){ return Math.hypot(this.x,this.y); }
len2(){ return this.x*this.x+this.y*this.y; }
norm(){ const l=this.len(); if(l){ this.x/=l; this.y/=l; } return this; }
dist(v){ return Math.hypot(this.x-v.x,this.y-v.y); }
lerp(v,t){ this.x=lerp(this.x,v.x,t); this.y=lerp(this.y,v.y,t); return this; }
}
/* ============ PARTICLE SYSTEM ============ */
class Particle {
constructor(x,y,color,life,vel,size,decay=1,gravity=0){
this.pos=new Vec(x,y); this.color=color; this.life=life; this.maxLife=life;
this.vel=vel.copy(); this.size=size; this.decay=decay; this.gravity=gravity; this.rotation=rand(0,Math.PI*2); this.rotSpeed=rand(-2,2);
}
update(dt){ this.pos.add(this.vel.copy().mul(dt)); this.vel.y+=this.gravity*dt; this.life-=dt*this.decay; this.rotation+=this.rotSpeed*dt; }
draw(ctx,alpha=1){ if(this.life<=0) return; const t=this.life/this.maxLife; ctx.save(); ctx.globalAlpha=t*alpha; ctx.translate(this.pos.x,this.pos.y); ctx.rotate(this.rotation); ctx.fillStyle=this.color; ctx.beginPath(); ctx.rect(-this.size/2,-this.size/2,this.size,this.size); ctx.fill(); ctx.restore(); }
alive(){ return this.life>0; }
}
class ParticleSystem {
constructor(){ this.particles=[]; }
add(p){ this.particles.push(p); }
emitBurst(x,y,count,color,lifeMin,lifeMax,velMin,velMax,sizeMin,sizeMax,gravity=0){
for(let i=0;i<count;i++){
const ang=rand(0,Math.PI*2), spd=rand(velMin,velMax);
this.add(new Particle(x,y,color,rand(lifeMin,lifeMax),new Vec(Math.cos(ang)*spd,Math.sin(ang)*spd),rand(sizeMin,sizeMax),1,gravity));
}
}
emitTrail(x,y,color,life,vel,size,count=1){
for(let i=0;i<count;i++){
this.add(new Particle(x+rand(-2,2),y+rand(-2,2),color,life,vel.copy().add(new Vec(rand(-10,10),rand(-10,10))),size,1,0));
}
}
update(dt){ for(let i=this.particles.length-1;i>=0;i--){ this.particles[i].update(dt); if(!this.particles[i].alive()) this.particles.splice(i,1); } }
draw(ctx,alpha=1){ this.particles.forEach(p=>p.draw(ctx,alpha)); }
clear(){ this.particles=[]; }
}
/* ============ CUSTOMER ============ */
const CustState = {
QUEUE:0, ENTERING:1, ORDERING:2, RECEIVING:3, HOLDING:4, EXITING:5, GONE:6
};
class Customer {
constructor(id, spawnX, spawnY){
this.id=id; this.pos=new Vec(spawnX,spawnY); this.target=new Vec(spawnX,spawnY);
this.state=CustState.QUEUE; this.isUnicorn=false; this.hasDrink=false;
this.serveTimer=0; this.serveTime=0; this.holdTimer=0; this.holdTime=0;
this.drinkColor='#8b5e3c'; this.drinkGlow=null;
this.wobble=rand(0,Math.PI*2); this.wobbleSpeed=rand(3,6);
this.bodyColor=hsl(rand(0,360),40,55); this.skinColor=hsl(rand(20,40),50,rand(45,75));
this.queueIndex=0; this.exitSide=rand(0,1); // 0 left, 1 right
}
setTarget(x,y){ this.target.set(x,y); }
update(dt, shop){
this.wobble+=this.wobbleSpeed*dt;
// movement
const dx=this.target.x-this.pos.x, dy=this.target.y-this.pos.y;
const dist=Math.hypot(dx,dy);
if(dist>1){
const speed=CFG.walkSpeed * (this.state===CustState.EXITING?1.2:1);
this.pos.x+=dx/dist*speed*dt;
this.pos.y+=dy/dist*speed*dt;
}else{ this.pos.set(this.target.x,this.target.y); }
// state logic
switch(this.state){
case CustState.QUEUE: break;
case CustState.ENTERING:
if(dist<2){ this.state=CustState.ORDERING; this.serveTime=randSeeded(CFG.serveTimeMin,CFG.serveTimeMax); this.serveTimer=0; }
break;
case CustState.ORDERING:
this.serveTimer+=dt;
if(this.serveTimer>=this.serveTime){ this.state=CustState.RECEIVING; }
break;
case CustState.RECEIVING:
// roll for unicorn HERE
this.isUnicorn=rollUnicorn();
this.hasDrink=true;
if(this.isUnicorn){
this.drinkColor=pick(CFG.neonColors);
this.drinkGlow=this.drinkColor;
shop.stats.unicornsProduced++;
shop.unicornsInside++;
}else{
this.drinkColor='#8b5e3c';
this.drinkGlow=null;
}
shop.stats.served++;
this.state=CustState.HOLDING;
this.holdTime=randSeeded(CFG.holdTimeMin,CFG.holdTimeMax);
this.holdTimer=0;
// move to hold area
const holdX=rand(shop.width*0.2, shop.width*0.8);
this.setTarget(holdX, shop.height*CFG.holdAreaY);
break;
case CustState.HOLDING:
this.holdTimer+=dt;
if(this.holdTimer>=this.holdTime){
this.state=CustState.EXITING;
const exitX=this.exitSide? shop.width*0.9 : shop.width*0.1;
this.setTarget(exitX, shop.height*CFG.exitY);
}
break;
case CustState.EXITING:
if(dist<2){ this.state=CustState.GONE; if(this.isUnicorn) shop.unicornsInside--; }
break;
}
}
draw(ctx, shakeX, shakeY){
const x=this.pos.x+shakeX, y=this.pos.y+shakeY;
const bob=Math.sin(this.wobble)*1.5;
ctx.save(); ctx.translate(x,y+bob);
// shadow
ctx.fillStyle='rgba(0,0,0,0.3)'; ctx.beginPath(); ctx.ellipse(0, this.customerRadius*0.8, this.customerRadius*0.7, 2, 0,0,Math.PI*2); ctx.fill();
// body
ctx.fillStyle=this.bodyColor; ctx.beginPath(); ctx.arc(0,0,this.customerRadius,0,Math.PI*2); ctx.fill();
// head
ctx.fillStyle=this.skinColor; ctx.beginPath(); ctx.arc(0,-this.customerRadius*0.7,this.customerRadius*0.65,0,Math.PI*2); ctx.fill();
// eyes
ctx.fillStyle='#222'; ctx.beginPath(); ctx.arc(-2,-this.customerRadius*0.7-1,1,0,Math.PI*2); ctx.arc(2,-this.customerRadius*0.7-1,1,0,Math.PI*2); ctx.fill();
// drink
if(this.hasDrink){
const drinkY=-this.customerRadius*1.6;
ctx.save(); ctx.translate(0,drinkY);
// cup
ctx.fillStyle='#fff'; ctx.beginPath(); ctx.moveTo(-5,0); ctx.lineTo(-4,10); ctx.lineTo(4,10); ctx.lineTo(5,0); ctx.closePath(); ctx.fill();
// liquid
ctx.fillStyle=this.drinkColor; ctx.beginPath(); ctx.arc(0,3,3.5,0,Math.PI*2); ctx.fill();
// glow
if(this.isUnicorn && this.drinkGlow){
const g=ctx.createRadialGradient(0,3,0,0,3,8); g.addColorStop(0,this.drinkGlow+'80'); g.addColorStop(1,this.drinkGlow+'00');
ctx.fillStyle=g; ctx.beginPath(); ctx.arc(0,3,10,0,Math.PI*2); ctx.fill();
// sparkles
ctx.fillStyle='#fff'; for(let i=0;i<3;i++){ const a=this.wobble*2+i*2; ctx.beginPath(); ctx.arc(Math.cos(a)*6,3+Math.sin(a)*6,1.5,0,Math.PI*2); ctx.fill(); }
}
ctx.restore();
}
ctx.restore();
}
}
/* ============ SHOP ============ */
class Shop {
constructor(canvas){
this.canvas=canvas; this.ctx=canvas.getContext('2d');
this.width=0; this.height=0; this.resize(); window.addEventListener('resize',()=>this.resize());
this.customers=[]; this.queue=[]; this.nextId=0; this.spawnTimer=0;
this.unicornsInside=0; this.peakUnicorns=0;
this.particles=new ParticleSystem();
this.shakeX=0; this.shakeY=0; this.shakeIntensity=0; this.shakeDecay=0;
this.colorShift=0; this.flashAlpha=0;
// catastrophe
this.phase='normal'; // normal, glitch1, glitch2, catastrophe, rebuilding
this.catTimer=0; this.catUnicorn=null; this.rebuildTimer=0;
this.buildingPieces=[]; this.rubbleParticles=new ParticleSystem();
// stats
this.stats={ served:0, unicornsProduced:0, catastrophes:0, simTime:0, lastCatTime:0 };
// input
this.speed=1; this.paused=false;
this.bindUI();
this.lastTime=performance.now();
requestAnimationFrame(t=>this.loop(t));
}
bindUI(){
$('#speed').addEventListener('input',e=>{ this.speed=parseFloat(e.target.value); $('#speedLabel').textContent=this.speed.toFixed(1)+'×'; });
$('#pauseBtn').addEventListener('click',()=>{ this.paused=!this.paused; $('#pauseBtn').textContent=this.paused?'▶':'⏸'; });
}
resize(){
const dpr=window.devicePixelRatio||1;
this.width=this.canvas.clientWidth; this.height=this.canvas.clientHeight;
this.canvas.width=this.width*dpr; this.canvas.height=this.height*dpr;
this.ctx.scale(dpr,dpr);
}
loop(now){
const dt=Math.min((now-this.lastTime)/1000, 0.1) * (this.paused?0:this.speed);
this.lastTime=now;
this.stats.simTime+=dt;
this.update(dt);
this.draw();
requestAnimationFrame(t=>this.loop(t));
}
update(dt){
// spawn customers
if(this.phase==='normal' || this.phase==='glitch1' || this.phase==='glitch2'){
this.spawnTimer-=dt;
if(this.spawnTimer<=0 && this.queue.length<CFG.maxQueue){
const insideCount=this.customers.filter(c=>c.state!==CustState.QUEUE && c.state!==CustState.GONE).length;
if(insideCount<CFG.maxInside){
const spawnX=this.width*CFG.queueStartX + (this.queue.length%6)*CFG.queueSpacing - (Math.floor(this.queue.length/6)*3*CFG.queueSpacing);
const spawnY=this.height*CFG.entranceY + Math.floor(this.queue.length/6)*30 + 20;
const c=new Customer(this.nextId++, spawnX, spawnY);
c.queueIndex=this.queue.length;
this.queue.push(c);
this.customers.push(c);
this.spawnTimer=randSeeded(CFG.spawnIntervalBase*0.5, CFG.spawnIntervalBase*1.5);
}else{
this.spawnTimer=0.2;
}
}
// move queue forward
this.updateQueue();
}
// update customers
for(let i=this.customers.length-1;i>=0;i--){
const c=this.customers[i];
if(c.state!==CustState.GONE) c.update(dt,this);
if(c.state===CustState.GONE){ this.customers.splice(i,1); }
}
// update peak
if(this.unicornsInside>this.peakUnicorns) this.peakUnicorns=this.unicornsInside;
// phase logic
this.updatePhase(dt);
// particles
this.particles.update(dt);
this.rubbleParticles.update(dt);
// shake decay
if(this.shakeIntensity>0){
this.shakeIntensity*=Math.exp(-this.shakeDecay*dt);
this.shakeX=(rng()-0.5)*this.shakeIntensity;
this.shakeY=(rng()-0.5)*this.shakeIntensity;
if(this.shakeIntensity<0.1){ this.shakeIntensity=0; this.shakeX=0; this.shakeY=0; }
}
// flash decay
if(this.flashAlpha>0){ this.flashAlpha*=Math.exp(-8*dt); if(this.flashAlpha<0.01) this.flashAlpha=0; }
// color shift
if(this.phase==='glitch1') this.colorShift=Math.sin(this.stats.simTime*8)*0.15;
else if(this.phase==='glitch2') this.colorShift=Math.sin(this.stats.simTime*15)*0.4;
else this.colorShift*=Math.exp(-3*dt);
// stats time since last cat
this.stats.lastCatTime+=dt;
this.updateHUD();
}
updateQueue(){
// find first customer in queue that can enter
for(let i=0;i<this.queue.length;i++){
const c=this.queue[i];
if(c.state===CustState.QUEUE){
const insideCount=this.customers.filter(c=>c.state!==CustState.QUEUE && c.state!==CustState.GONE).length;
if(insideCount<CFG.maxInside){
c.state=CustState.ENTERING;
c.setTarget(this.width*0.5, this.height*CFG.counterY);
this.queue.splice(i,1);
// re-index remaining queue
this.queue.forEach((c,j)=>{ c.queueIndex=j; this.updateQueuePos(c); });
break;
}
}
}
// update queue positions
this.queue.forEach((c,i)=>{ this.updateQueuePos(c); });
}
updateQueuePos(c){
const col=c.queueIndex%6, row=Math.floor(c.queueIndex/6);
const targetX=this.width*CFG.queueStartX + col*CFG.queueSpacing - row*3*CFG.queueSpacing;
const targetY=this.height*CFG.entranceY + row*30 + 20;
c.setTarget(targetX, targetY);
}
updatePhase(dt){
const prevPhase=this.phase;
if(this.phase==='normal' || this.phase==='glitch1' || this.phase==='glitch2'){
if(this.unicornsInside>=3){ this.triggerCatastrophe(); }
else if(this.unicornsInside===2){ this.phase='glitch2'; this.shakeIntensity=8; this.shakeDecay=0.5; this.flashAlpha=0.3; }
else if(this.unicornsInside===1){ this.phase='glitch1'; this.shakeIntensity=2; this.shakeDecay=1; }
else { this.phase='normal'; }
}else if(this.phase==='catastrophe'){
this.catTimer+=dt;
this.updateCatastrophe(dt);
if(this.catTimer>=CFG.catastropheDuration){
this.phase='rebuilding'; this.rebuildTimer=0; this.rubbleParticles.clear(); this.particles.clear();
}
}else if(this.phase==='rebuilding'){
this.rebuildTimer+=dt;
if(this.rebuildTimer>=CFG.rebuildDuration){
this.phase='normal'; this.colorShift=0; this.flashAlpha=0; this.shakeIntensity=0;
// clear all customers, reset queue
this.customers.forEach(c=>{ if(c.state!==CustState.GONE) c.state=CustState.GONE; });
this.queue=[]; this.unicornsInside=0; this.spawnTimer=0;
this.catUnicorn=null;
}
}
if(prevPhase!==this.phase && this.phase==='catastrophe'){
$('#eventBanner').classList.add('show');
setTimeout(()=>$('#eventBanner').classList.remove('show'), 2000);
}
}
triggerCatastrophe(){
this.phase='catastrophe'; this.catTimer=0; this.stats.catastrophes++; this.stats.lastCatTime=0;
this.shakeIntensity=30; this.shakeDecay=0.3; this.flashAlpha=1;
// create building pieces
this.buildingPieces=[];
const cols=8, rows=6;
for(let r=0;r<rows;r++) for(let c=0;c<cols;c++){
const x=this.width*(c+0.5)/cols, y=this.height*(r+0.5)/rows;
const w=this.width/cols*0.9, h=this.height/rows*0.9;
const ang=rand(-0.3,0.3), vx=rand(-200,200), vy=rand(-300,-50);
this.buildingPieces.push({x,y,w,h,ang,vx,vy,vy0:vy,color:hsl(rand(200,260),30,20),rotSpeed:rand(-2,2)});
}
// spawn giant unicorn
this.catUnicorn={ x:this.width/2, y:this.height*1.2, targetY:this.height*0.45, scale:0, rotation:0, legPhase:0, hornGlow:0, dancePhase:0 };
// massive particle burst
this.particles.emitBurst(this.width/2,this.height/2,200,pick(CFG.neonColors),1,3,100,400,2,8,-50);
this.particles.emitBurst(this.width/2,this.height/2,100,'#fff',0.5,2,200,500,1,4,-100);
}
updateCatastrophe(dt){
const t=this.catTimer, dur=CFG.catastropheDuration;
const progress=t/dur;
// building pieces fall
this.buildingPieces.forEach(p=>{
p.vy+=800*dt; p.x+=p.vx*dt; p.y+=p.vy*dt; p.ang+=p.rotSpeed*dt;
if(p.y>this.height+100 && rng()<0.02){
this.rubbleParticles.emitBurst(p.x,p.y,5,p.color,0.2,0.5,20,80,2,6,200);
}
});
// unicorn rises
if(this.catUnicorn){
if(this.catUnicorn.y>this.catUnicorn.targetY){
this.catUnicorn.y-=150*dt;
if(this.catUnicorn.y<this.catUnicorn.targetY) this.catUnicorn.y=this.catUnicorn.targetY;
}
this.catUnicorn.scale=Math.min(1, this.catUnicorn.scale+dt*0.8);
// dance after risen
if(this.catUnicorn.y<=this.catUnicorn.targetY+5){
this.catUnicorn.dancePhase+=dt*2;
this.catUnicorn.rotation=Math.sin(this.catUnicorn.dancePhase)*0.3;
this.catUnicorn.legPhase+=dt*8;
this.catUnicorn.hornGlow=0.5+0.5*Math.sin(this.catUnicorn.dancePhase*3);
// emit sparkles
if(rng()<0.3){
this.particles.emitTrail(
this.catUnicorn.x+rand(-40,40)*this.catUnicorn.scale,
this.catUnicorn.y+rand(-60,20)*this.catUnicorn.scale,
pick(CFG.neonColors), rand(0.3,1), new Vec(rand(-50,50),rand(-100,10)), rand(2,6), 1
);
}
}
}
// screen effects
this.shakeIntensity=30*(1-progress*0.5)+10*Math.sin(t*40);
this.flashAlpha=0.5+0.5*Math.sin(t*30);
this.colorShift=Math.sin(t*60)*0.8;
}
draw(){
const ctx=this.ctx; const w=this.width, h=this.height;
ctx.save();
// apply shake
ctx.translate(this.shakeX, this.shakeY);
// color shift via filter hack - draw to temp canvas? simpler: just tint overlay
ctx.clearRect(0,0,w,h);
// background
const bgGrad=ctx.createLinearGradient(0,0,0,h);
bgGrad.addColorStop(0,'#0a0a12'); bgGrad.addColorStop(1,'#151522');
ctx.fillStyle=bgGrad; ctx.fillRect(0,0,w,h);
// draw shop interior (normal/rebuilding)
if(this.phase!=='catastrophe'){
this.drawShop(ctx);
}else{
this.drawCatastrophe(ctx);
}
// draw customers (except during catastrophe)
if(this.phase!=='catastrophe'){
this.customers.forEach(c=>{ if(c.state!==CustState.QUEUE && c.state!==CustState.GONE) c.draw(ctx,this.shakeX,this.shakeY); });
// queue customers
this.queue.forEach(c=>c.draw(ctx,this.shakeX,this.shakeY));
}
// particles
this.particles.draw(ctx);
this.rubbleParticles.draw(ctx);
// flash overlay
if(this.flashAlpha>0){
ctx.fillStyle=`rgba(255,0,255,${this.flashAlpha*0.6})`; ctx.fillRect(0,0,w,h);
}
// color shift overlay
if(Math.abs(this.colorShift)>0.01){
ctx.globalCompositeOperation='hue';
ctx.fillStyle=hsl(this.colorShift*180,100,50);
ctx.globalAlpha=Math.abs(this.colorShift)*0.3;
ctx.fillRect(0,0,w,h);
ctx.globalAlpha=1; ctx.globalCompositeOperation='source-over';
}
ctx.restore();
}
drawShop(ctx){
const w=this.width, h=this.height;
// floor
const floorY=h*0.92;
ctx.fillStyle='#1a1a28'; ctx.fillRect(0,floorY,w,h-floorY);
// walls
ctx.fillStyle='#12121e'; ctx.fillRect(0,0,w,h*0.92);
// counter
const counterY=h*CFG.counterY;
ctx.fillStyle='#2a2a3a'; ctx.fillRect(w*0.15, counterY-10, w*0.7, 30);
ctx.fillStyle='#3a3a4a'; ctx.fillRect(w*0.15, counterY-10, w*0.7, 8);
// register
ctx.fillStyle='#222'; ctx.fillRect(w*0.5-20, counterY-8, 40, 16);
ctx.fillStyle='#0f0'; ctx.fillRect(w*0.5-15, counterY-4, 30, 8);
// menu boards
ctx.fillStyle='#1a1a2a'; ctx.fillRect(w*0.1, h*0.05, w*0.35, h*0.06);
ctx.fillRect(w*0.55, h*0.05, w*0.35, h*0.06);
// neon trim
this.drawNeonLine(ctx, w*0.15, counterY-10, w*0.85, counterY-10, '#00ffff', 2);
this.drawNeonLine(ctx, w*0.15, counterY+20, w*0.85, counterY+20, '#ff00ff', 2);
// pickup area
const pickupY=h*CFG.pickupY;
ctx.fillStyle='#1e1e2e'; ctx.fillRect(w*0.2, pickupY-15, w*0.6, 30);
// entrance/exit
ctx.fillStyle='#0a0a12'; ctx.fillRect(w*0.4, h*0.88, w*0.2, h*0.12);
// door frame
ctx.strokeStyle='#333'; ctx.lineWidth=3; ctx.strokeRect(w*0.4, h*0.88, w*0.2, h*0.12);
// glitch effects
if(this.phase==='glitch1'){
// subtle scanlines
ctx.strokeStyle='rgba(0,255,255,0.08)'; ctx.lineWidth=1;
for(let y=0;y<h;y+=4){ ctx.beginPath(); ctx.moveTo(0,y); ctx.lineTo(w,y); ctx.stroke(); }
}else if(this.phase==='glitch2'){
// heavy scanlines + color fringing
ctx.strokeStyle='rgba(255,0,255,0.15)'; ctx.lineWidth=1;
for(let y=0;y<h;y+=2){ ctx.beginPath(); ctx.moveTo(0,y); ctx.lineTo(w,y); ctx.stroke(); }
// random glitch blocks
for(let i=0;i<5;i++){
const x=rand(0,w), y=rand(0,h), bw=rand(20,100), bh=rand(2,8);
ctx.fillStyle=pick(['#ff00ff','#00ffff','#ffff00'])+'40'; ctx.fillRect(x,y,bw,bh);
}
}
}
drawNeonLine(ctx,x1,y1,x2,y2,color,width){
ctx.save();
ctx.shadowColor=color; ctx.shadowBlur=15;
ctx.strokeStyle=color; ctx.lineWidth=width; ctx.lineCap='round';
ctx.beginPath(); ctx.moveTo(x1,y1); ctx.lineTo(x2,y2); ctx.stroke();
ctx.shadowBlur=0; ctx.restore();
}
drawCatastrophe(ctx){
const w=this.width, h=this.height;
// debris
this.buildingPieces.forEach(p=>{
ctx.save(); ctx.translate(p.x,p.y); ctx.rotate(p.ang);
ctx.fillStyle=p.color; ctx.fillRect(-p.w/2,-p.h/2,p.w,p.h);
// neon edges
ctx.strokeStyle='#ff00ff'; ctx.lineWidth=2; ctx.shadowColor='#ff00ff'; ctx.shadowBlur=10;
ctx.strokeRect(-p.w/2,-p.h/2,p.w,p.h);
ctx.restore();
});
// giant unicorn
if(this.catUnicorn && this.catUnicorn.scale>0.1){
this.drawGiantUnicorn(ctx, this.catUnicorn);
}
// rubble particles
this.rubbleParticles.draw(ctx);
}
drawGiantUnicorn(ctx, u){
const s=u.scale*1.5; const x=u.x, y=u.y;
ctx.save(); ctx.translate(x,y); ctx.rotate(u.rotation); ctx.scale(s,s);
// body
const grad=ctx.createLinearGradient(-60,-40,60,40);
grad.addColorStop(0,'#ff00ff'); grad.addColorStop(0.5,'#00ffff'); grad.addColorStop(1,'#ffff00');
ctx.fillStyle=grad;
ctx.beginPath();
ctx.ellipse(0,0,50,35,0,0,Math.PI*2); ctx.fill();
// legs
ctx.strokeStyle='#fff'; ctx.lineWidth=6; ctx.lineCap='round';
for(let i=-1;i<=1;i+=2){
const legX=i*25;
ctx.beginPath(); ctx.moveTo(legX,35);
const kneeY=35+30+Math.sin(u.legPhase+i)*15;
ctx.quadraticCurveTo(legX+Math.sin(u.legPhase)*10, kneeY, legX, 80);
ctx.stroke();
// hoof
ctx.fillStyle='#ff00ff'; ctx.beginPath(); ctx.arc(legX,80,8,0,Math.PI*2); ctx.fill();
}
// neck & head
ctx.strokeStyle='#fff'; ctx.lineWidth=10; ctx.lineCap='round';
ctx.beginPath(); ctx.moveTo(40, -10);
const headX=40+Math.sin(u.dancePhase)*15, headY=-70+Math.cos(u.dancePhase*0.7)*10;
ctx.quadraticCurveTo(55, -40, headX, headY); ctx.stroke();
// head
ctx.fillStyle=grad; ctx.beginPath(); ctx.arc(headX, headY, 22, 0, Math.PI*2); ctx.fill();
// eye
ctx.fillStyle='#000'; ctx.beginPath(); ctx.arc(headX+8, headY-5, 5, 0, Math.PI*2); ctx.fill();
ctx.fillStyle='#fff'; ctx.beginPath(); ctx.arc(headX+10, headY-7, 2, 0, Math.PI*2); ctx.fill();
// horn
const hornH=50+Math.sin(u.dancePhase*2)*10;
const hornGrad=ctx.createLinearGradient(headX-5, headY-20, headX+5, headY-20-hornH);
hornGrad.addColorStop(0,'#ff00ff'); hornGrad.addColorStop(0.5,'#00ffff'); hornGrad.addColorStop(1,'#ffff00');
ctx.fillStyle=hornGrad; ctx.shadowColor='#ff00ff'; ctx.shadowBlur=30*u.hornGlow;
ctx.beginPath(); ctx.moveTo(headX-8, headY-20); ctx.lineTo(headX, headY-20-hornH); ctx.lineTo(headX+8, headY-20); ctx.closePath(); ctx.fill();
ctx.shadowBlur=0;
// mane
ctx.strokeStyle='#ff00ff'; ctx.lineWidth=4; ctx.lineCap='round';
for(let i=0;i<5;i++){
const mx=headX-15+i*3, my=headY-20;
ctx.beginPath(); ctx.moveTo(mx, my);
ctx.quadraticCurveTo(mx+Math.sin(u.dancePhase+i)*20, my-30, mx+Math.sin(u.dancePhase+i)*30, my-60);
ctx.stroke();
}
// tail
ctx.strokeStyle='#00ffff'; ctx.lineWidth=5;
ctx.beginPath(); ctx.moveTo(-50, 10);
ctx.quadraticCurveTo(-80+Math.sin(u.dancePhase)*15, 20+Math.cos(u.dancePhase)*10, -90, 50);
ctx.stroke();
// aura particles
for(let i=0;i<8;i++){
const a=u.dancePhase*2+i*0.8, r=70+Math.sin(a*3)*20;
ctx.fillStyle=pick(CFG.neonColors); ctx.globalAlpha=0.6;
ctx.beginPath(); ctx.arc(Math.cos(a)*r, Math.sin(a)*r, 4, 0, Math.PI*2); ctx.fill();
}
ctx.globalAlpha=1; ctx.restore();
}
updateHUD(){
$('#statServed').textContent=this.stats.served;
$('#statUnicorns').textContent=this.stats.unicornsProduced;
const rate=this.stats.served? (this.stats.unicornsProduced/this.stats.served*100).toFixed(2)+'%' : '0.00%';
$('#statRate').textContent=rate;
$('#statCatas').textContent=this.stats.catastrophes;
$('#statInside').textContent=this.unicornsInside;
$('#statPeak').textContent=this.peakUnicorns;
$('#statSince').textContent=this.stats.lastCatTime.toFixed(1)+'s';
$('#statTime').textContent=this.stats.simTime.toFixed(1)+'s';
$('#statQueue').textContent=this.queue.length;
const insideCount=this.customers.filter(c=>c.state!==CustState.QUEUE && c.state!==CustState.GONE).length;
$('#statInsideCount').textContent=`${insideCount}/${CFG.maxInside}`;
}
}
/* ============ INIT ============ */
window.addEventListener('load',()=>{
const canvas=$('#canvas');
// ensure canvas fills space
function fit(){ canvas.style.width='100%'; canvas.style.height='100%'; }
fit(); window.addEventListener('resize',fit);
new Shop(canvas);
});
</script>
</body>
</html>
In the first few minutes at 1× speed you’ll see a trickle of dot-customers queue outside, filter through the door (max 8 inside), get served at the counter, and mill around with brown drinks. Roughly every 6th drink flashes neon — the roll happens exactly once per customer at the RECEIVING state, using the seeded RNG. The HUD counters climb: Served, Unicorns Produced, Observed Rate converging toward 16.67%.
When a unicorn enters the hold area, unicornsInside increments. At 1 the shop gets a faint cyan/magenta neon trim and subtle scanlines. At 2 the scanlines thicken, the building shudders (screen shake ~8px), random glitch blocks flicker, and the color-shift overlay warms up. At 3 the catastrophe triggers instantly: the shop fractures into 48 rotating rectangles that fall with gravity, a giant procedural neon unicorn rises from below, scales up, then dances (bobbing head, prancing legs, glowing spiraling horn, flowing mane/tail, orbiting sparkle particles) while the screen shakes violently and flashes magenta. After ~8 seconds the debris fades, the unicorn dissolves, the shop rebuilds over 3 seconds, all customers vanish, the queue clears, and spawning resumes.
Tuned / special-cased:
Date.now() at load).rollUnicorn() is a single rng() < 1/6 call at the moment the drink is handed over.Known rough edges:
hue blend mode hack; on some browsers it’s subtle.dt is clamped to 0.1s but multiplied by speed — debris can tunnel through the floor.Leave it running at 10×: you’ll see a catastrophe roughly every 30–40 real seconds, the Observed Rate will wobble around 16.67%, and the Peak Unicorns Inside will mostly sit at 1–2 with occasional 3s that trigger the event.