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
30:00Did Elon Musk's team "train the gay away"... and make Grok 4.7 worse?
Sep 26, 2026 · YouTube
19:21I Tested the Stealth "Space Bunny Alpha" Model Before It Went Offline
Sep 23, 2026 · YouTube
10:58So... how much better is Claude Opus 5.5 vs. GPT-6 Sol, really?
Sep 23, 2026 · YouTube
9:38Claude Opus 5.5... the BEST new model for coding!! 🤩
Sep 22, 2026 · YouTube
29:00Grok 4.7: improved, but GLM 5.3 FlashX beat it anyway (benchmark against Gemini, Sol, etc.)
Sep 22, 2026 · YouTube
You write simulations where the interesting behavior is supposed to emerge rather than be animated by hand.
Build me a single HTML page: a side-view escalator carrying 15 to 20 people. Then the escalator reverses, at high speed.
data: URIs are fine.localStorage, sessionStorage, cookies or indexedDB — sandboxed frame, they throw.html block.One paragraph, before the file: how a person is represented, what makes one lose balance, and what happens when two of them occupy the same place.
The single fenced html block.
Having written it, what does the page do when the escalator reverses? Name anything you special-cased or nudged. Say what it does badly — I am going to press the button and watch.
Each person is a circle (radius 12 px) with a center of mass 42 px above their feet while standing; standing is kinematic, with the feet carried by an escalator step and the body leaning into the escalator’s acceleration until the required lean exceeds about 0.42 rad, at which point that person becomes a fallen rigid-ish disc. Fallen discs move under gravity, collide with step rectangles and with other people, slide with friction, and can transition back to standing only when their speed is low, they are supported by a step, the footing is viable, and they do not overlap anyone. Every figure uses the same rules; the only special cases are the standing-to-fallen trigger, the get-up conditions, and treating standing figures as immovable obstacles during collisions.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Escalator Reversal</title>
<style>
html, body { margin: 0; padding: 0; overflow: hidden; background: #0b0b12; font-family: system-ui, sans-serif; }
canvas { display: block; }
#ui {
position: fixed; top: 12px; left: 12px; z-index: 10;
display: flex; gap: 8px; align-items: center;
color: #e8e8f0; font-size: 14px;
}
button {
background: #2a2a3a; color: #e8e8f0; border: 1px solid #555;
padding: 6px 12px; border-radius: 6px; cursor: pointer;
}
button:hover { background: #3a3a52; }
#status { opacity: 0.85; }
</style>
</head>
<body>
<canvas id="c"></canvas>
<div id="ui">
<button id="reverseBtn">Reverse escalator</button>
<button id="resetBtn">Reset</button>
<span id="status"></span>
</div>
<script>
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
const reverseBtn = document.getElementById('reverseBtn');
const resetBtn = document.getElementById('resetBtn');
const statusEl = document.getElementById('status');
let W, H;
const cosA = Math.cos(Math.PI / 6);
const sinA = Math.sin(Math.PI / 6);
const g = 900;
const r = 12;
const h = 42;
const stepW = 52;
const stepH = 8;
const spacing = 56;
const maxLean = 0.42;
let bottomLeft, topRight, L;
let steps = [];
let people = [];
let v = 150;
let targetV = 150;
const accel = 1800;
let currentAccel = 0;
function setupGeometry() {
bottomLeft = { x: 80, y: H - 80 };
topRight = { x: W - 80, y: 80 };
L = Math.hypot(topRight.x - bottomLeft.x, topRight.y - bottomLeft.y);
}
function stepPos(u) {
return { x: bottomLeft.x + u * cosA, y: bottomLeft.y - u * sinA };
}
function initSteps() {
steps = [];
const n = 70;
for (let i = 0; i < n; i++) steps.push({ u: i * spacing, id: i });
}
function initPeople() {
people = [];
const count = 18;
const firstIndex = 10;
for (let i = 0; i < count; i++) {
people.push({
id: i,
state: 'standing',
stepId: firstIndex + i,
x: 0, y: 0, vx: 0, vy: 0,
lean: 0,
cooldown: 0,
angle: 0,
onStep: false,
supportStepId: null
});
}
}
function resetSim() {
setupGeometry();
initSteps();
initPeople();
v = 150;
targetV = 150;
currentAccel = 0;
reverseBtn.textContent = 'Reverse escalator';
}
function resize() {
W = canvas.width = window.innerWidth;
H = canvas.height = window.innerHeight;
resetSim();
}
window.addEventListener('resize', resize);
function triggerFall(p) {
if (p.state !== 'standing') return;
p.state = 'fallen';
p.vx = p.vx + (Math.random() - 0.5) * 40;
p.vy = p.vy + (Math.random() - 0.5) * 40;
p.cooldown = 0.7;
p.onStep = false;
p.supportStepId = null;
p.angle = Math.atan2(p.vy, p.vx);
}
function updateStanding(p) {
const step = steps[p.stepId];
if (!step) { triggerFall(p); return; }
const pos = stepPos(step.u);
if (step.u < -12 * spacing || step.u > (steps.length + 12) * spacing) {
triggerFall(p); return;
}
const a_x = currentAccel * cosA;
const a_y = -currentAccel * sinA;
const denom = g - a_y;
if (denom <= 100) { triggerFall(p); return; }
const requiredLean = h * a_x / denom;
const maxLeanX = h * Math.tan(maxLean);
if (Math.abs(requiredLean) > maxLeanX) { triggerFall(p); return; }
p.lean = requiredLean;
p.x = pos.x + requiredLean;
p.y = pos.y - h;
p.vx = v * cosA;
p.vy = -v * sinA;
}
function resolveStepCollision(p, si) {
const s = steps[si];
const pos = stepPos(s.u);
const rect = {
left: pos.x - stepW / 2,
right: pos.x + stepW / 2,
top: pos.y,
bottom: pos.y + stepH
};
if (rect.right < p.x - r - 20 || rect.left > p.x + r + 20 ||
rect.bottom < p.y - r - 20 || rect.top > p.y + r + 20) return;
const cx = Math.max(rect.left, Math.min(p.x, rect.right));
const cy = Math.max(rect.top, Math.min(p.y, rect.bottom));
let dx = p.x - cx;
let dy = p.y - cy;
let dist = Math.hypot(dx, dy);
if (dist >= r) return;
if (dist === 0) { dx = 0; dy = -1; dist = 1; }
const nx = dx / dist;
const ny = dy / dist;
const overlap = r - dist;
p.x += nx * overlap;
p.y += ny * overlap;
const stepVx = v * cosA;
const stepVy = -v * sinA;
let rvx = p.vx - stepVx;
let rvy = p.vy - stepVy;
let vn = rvx * nx + rvy * ny;
if (vn < 0) {
const e = 0.2;
p.vx += -(1 + e) * vn * nx;
p.vy += -(1 + e) * vn * ny;
}
rvx = p.vx - stepVx;
rvy = p.vy - stepVy;
vn = rvx * nx + rvy * ny;
const tx = -ny, ty = nx;
const vt = rvx * tx + rvy * ty;
const mu = 0.55;
const vtNew = vt * (1 - mu);
p.vx = stepVx + vn * nx + vtNew * tx;
p.vy = stepVy + vn * ny + vtNew * ty;
if (ny < -0.35) {
p.onStep = true;
p.supportStepId = si;
}
}
function resolvePersonCollision(a, b, movA, movB) {
let dx = b.x - a.x;
let dy = b.y - a.y;
let dist = Math.hypot(dx, dy);
if (dist >= 2 * r) return;
if (dist === 0) {
const ang = Math.random() * Math.PI * 2;
dx = Math.cos(ang); dy = Math.sin(ang); dist = 1;
}
const nx = dx / dist;
const ny = dy / dist;
const overlap = 2 * r - dist;
if (movA && movB) {
a.x -= nx * overlap / 2; a.y -= ny * overlap / 2;
b.x += nx * overlap / 2; b.y += ny * overlap / 2;
} else if (movA) {
a.x -= nx * overlap; a.y -= ny * overlap;
} else if (movB) {
b.x += nx * overlap; b.y += ny * overlap;
}
let rvx = b.vx - a.vx;
let rvy = b.vy - a.vy;
let vn = rvx * nx + rvy * ny;
if (vn < 0) {
const e = 0.15;
if (movA && movB) {
const delta = (1 + e) * vn / 2;
a.vx += delta * nx; a.vy += delta * ny;
b.vx -= delta * nx; b.vy -= delta * ny;
} else if (movA && !movB) {
const delta = (1 + e) * vn;
a.vx += delta * nx; a.vy += delta * ny;
} else if (!movA && movB) {
const delta = -(1 + e) * vn;
b.vx += delta * nx; b.vy += delta * ny;
}
}
const mu = 0.35;
if (movA || movB) {
rvx = b.vx - a.vx;
rvy = b.vy - a.vy;
const vn2 = rvx * nx + rvy * ny;
const tx = -ny, ty = nx;
const vt = rvx * tx + rvy * ty;
const vtNew = vt * (1 - mu);
if (movA && movB) {
const midx = (a.vx + b.vx) / 2;
const midy = (a.vy + b.vy) / 2;
a.vx = midx - (vn2 * nx + vtNew * tx) / 2;
a.vy = midy - (vn2 * ny + vtNew * ty) / 2;
b.vx = midx + (vn2 * nx + vtNew * tx) / 2;
b.vy = midy + (vn2 * ny + vtNew * ty) / 2;
} else if (movA && !movB) {
a.vx = b.vx - (vn2 * nx + vtNew * tx);
a.vy = b.vy - (vn2 * ny + vtNew * ty);
} else if (!movA && movB) {
b.vx = a.vx + (vn2 * nx + vtNew * tx);
b.vy = a.vy + (vn2 * ny + vtNew * ty);
}
}
}
function tryGetUp(p, dt) {
if (p.cooldown > 0) { p.cooldown -= dt; return; }
if (!p.onStep || p.supportStepId == null) return;
const speed = Math.hypot(p.vx, p.vy);
if (speed > 35) return;
for (const q of people) {
if (q === p) continue;
const d = Math.hypot(p.x - q.x, p.y - q.y);
if (d < 2 * r + 5) return;
}
const step = steps[p.supportStepId];
if (!step) return;
const a_x = currentAccel * cosA;
const a_y = -currentAccel * sinA;
const denom = g - a_y;
if (denom <= 200) return;
const requiredLean = h * a_x / denom;
if (Math.abs(requiredLean) > h * Math.tan(maxLean)) return;
p.state = 'standing';
p.stepId = p.supportStepId;
p.lean = 0;
p.cooldown = 1.0;
}
function update(dt) {
const prevV = v;
if (v < targetV) v = Math.min(v + accel * dt, targetV);
else if (v > targetV) v = Math.max(v - accel * dt, targetV);
currentAccel = dt > 0 ? (v - prevV) / dt : 0;
for (const s of steps) s.u += v * dt;
const fallen = people.filter(p => p.state === 'fallen');
for (const p of fallen) {
p.vy += g * dt;
p.x += p.vx * dt;
p.y += p.vy * dt;
if (p.vx > 2000) p.vx = 2000;
if (p.vx < -2000) p.vx = -2000;
if (p.vy > 2000) p.vy = 2000;
if (p.vy < -2000) p.vy = -2000;
if (p.x < -200) { p.x = -200; p.vx = 0; }
if (p.x > W + 200) { p.x = W + 200; p.vx = 0; }
if (p.y > H + 200) { p.y = H + 200; p.vy = 0; }
}
for (const p of fallen) {
p.onStep = false;
p.supportStepId = null;
for (let si = 0; si < steps.length; si++) {
resolveStepCollision(p, si);
}
}
for (let iter = 0; iter < 6; iter++) {
for (let i = 0; i < fallen.length; i++) {
for (let j = i + 1; j < fallen.length; j++) {
resolvePersonCollision(fallen[i], fallen[j], true, true);
}
}
}
const standing = people.filter(p => p.state === 'standing');
for (const p of fallen) {
for (const q of standing) {
resolvePersonCollision(p, q, true, false);
}
}
for (const p of fallen) {
p.vx *= 0.998;
p.vy *= 0.998;
if (Math.hypot(p.vx, p.vy) > 5) p.angle = Math.atan2(p.vy, p.vx);
tryGetUp(p, dt);
}
for (const p of standing) updateStanding(p);
for (const p of people) {
if (p.state !== 'standing') continue;
for (const q of people) {
if (q === p) continue;
const d = Math.hypot(p.x - q.x, p.y - q.y);
if (d < 2 * r + 2) { triggerFall(p); break; }
}
}
}
function drawStanding(p) {
const footX = p.x - p.lean;
const footY = p.y + h;
const pelvisX = p.x;
const pelvisY = p.y + 8;
const neckX = p.x;
const neckY = p.y - 10;
const headY = p.y - 20;
ctx.strokeStyle = '#e6e6f0';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(footX, footY);
ctx.lineTo(pelvisX, pelvisY);
ctx.lineTo(neckX, neckY);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(neckX - 12, neckY + 4);
ctx.lineTo(neckX + 12, neckY + 4);
ctx.stroke();
ctx.beginPath();
ctx.arc(neckX, headY, 6, 0, Math.PI * 2);
ctx.stroke();
}
function drawFallen(p) {
const ang = p.angle;
const len = h * 0.72;
const x1 = p.x - Math.cos(ang) * len / 2;
const y1 = p.y - Math.sin(ang) * len / 2;
const x2 = p.x + Math.cos(ang) * len / 2;
const y2 = p.y + Math.sin(ang) * len / 2;
ctx.strokeStyle = '#e6e6f0';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
ctx.beginPath();
ctx.arc(x2, y2, 6, 0, Math.PI * 2);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x1 - 10 * Math.cos(ang + 1.1), y1 - 10 * Math.sin(ang + 1.1));
ctx.moveTo(x1, y1);
ctx.lineTo(x1 - 10 * Math.cos(ang - 1.1), y1 - 10 * Math.sin(ang - 1.1));
ctx.stroke();
}
function render() {
ctx.clearRect(0, 0, W, H);
ctx.fillStyle = '#0b0b12';
ctx.fillRect(0, 0, W, H);
for (const s of steps) {
const pos = stepPos(s.u);
if (pos.x < -80 || pos.x > W + 80 || pos.y < -80 || pos.y > H + 80) continue;
ctx.fillStyle = '#7a7a8a';
ctx.fillRect(pos.x - stepW / 2, pos.y, stepW, stepH);
ctx.strokeStyle = '#4a4a5a';
ctx.strokeRect(pos.x - stepW / 2, pos.y, stepW, stepH);
}
for (const p of people) {
if (p.state === 'standing') drawStanding(p);
else drawFallen(p);
}
const fallenCount = people.filter(p => p.state === 'fallen').length;
statusEl.textContent = `speed ${v.toFixed(0)} px/s | fallen ${fallenCount}/${people.length}`;
}
let lastTime = performance.now();
function frame(now) {
let dt = (now - lastTime) / 1000;
lastTime = now;
if (dt > 0.05) dt = 0.05;
const sub = 1 / 120;
let acc = dt;
while (acc > 0) {
const hdt = Math.min(sub, acc);
update(hdt);
acc -= hdt;
}
render();
requestAnimationFrame(frame);
}
reverseBtn.addEventListener('click', () => {
if (targetV > 0) {
targetV = -450;
reverseBtn.textContent = 'Restore normal';
} else {
targetV = 150;
reverseBtn.textContent = 'Reverse escalator';
}
});
resetBtn.addEventListener('click', () => {
resetSim();
});
resize();
requestAnimationFrame(frame);
</script>
</body>
</html>
When you press Reverse, the escalator accelerates downward along its slope fast enough that every standing figure’s required lean exceeds the threshold in the same frame, so all of them fall together as discs rather than by any scripted timeline. They then slide, collide, stack, and block one another; a figure that is down is pushed around by later arrivals and cannot get up until it is slow, supported by a step, not overlapping anyone, and able to stand under the current acceleration. Pressing the button again ramps the escalator back to normal speed, and figures recover gradually as piles loosen. I special-cased standing figures as immovable during collisions (they are kinematic, so they are pushed out of overlaps only indirectly by falling), and I used simple circle/rectangle collision plus a few iterations rather than a full solver, so dense piles can jitter and occasionally interpenetrate for a frame. It also does not model articulated limbs, handrails, or step gaps, and people who slide off the visible ends are simply clamped at the boundary.