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 rigid capsule with position, velocity, angle and spin, plus a limited balancing torque that tries to keep them upright. The moving belt applies friction at floor contacts; a fast reversal can pull the feet out faster than that balancing torque can compensate. Overlapping capsules are separated and exchange collision impulses, so a fallen person continues to obstruct and push other people. A grounded person can try to get upright again, with less leverage when crowded.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Escalator reversal</title>
<style>
:root {
color-scheme: dark;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
background: #10181d;
color: #e8efed;
}
* { box-sizing: border-box; }
body { margin: 0; }
main { max-width: 1100px; margin: 0 auto; padding: 24px 20px 30px; }
header { display: flex; flex-wrap: wrap; align-items: end; justify-content: space-between; gap: 12px; }
h1 { margin: 0; font-size: clamp(1.5rem, 3vw, 2.2rem); letter-spacing: -.04em; }
.intro { margin: 6px 0 0; color: #aabbb8; line-height: 1.45; }
.controls { display: flex; align-items: center; flex-wrap: wrap; gap: 9px; }
button {
border: 1px solid #8ecdbd;
border-radius: 7px;
padding: 10px 15px;
background: #a6e1cb;
color: #10241f;
font: inherit;
font-weight: 700;
cursor: pointer;
}
button:hover { background: #c4f2df; }
button:focus-visible { outline: 3px solid #fff; outline-offset: 3px; }
button.secondary { background: #24353a; color: #e3eeea; border-color: #536a69; }
button.secondary:hover { background: #34494d; }
.scene {
margin-top: 20px;
border: 1px solid #34484a;
border-radius: 12px;
overflow: hidden;
background: #17262b;
}
canvas { display: block; width: 100%; aspect-ratio: 1.85; }
.foot {
display: flex;
justify-content: space-between;
gap: 15px;
flex-wrap: wrap;
margin-top: 12px;
color: #aabbb8;
font-size: .88rem;
line-height: 1.5;
}
.status { color: #d4e5df; font-weight: 650; }
.dot { display: inline-block; width: 8px; height: 8px; margin-right: 7px; border-radius: 50%; background: #a6e1cb; }
.dot.reverse { background: #ffb26e; }
</style>
</head>
<body>
<main>
<header>
<div>
<h1>Escalator reversal</h1>
<p class="intro">A conveyor, a crowd, and a rather abrupt direction switch.</p>
</div>
<div class="controls">
<button id="reverse" type="button" aria-pressed="false">Reverse escalator</button>
<button id="reset" class="secondary" type="button">Reset</button>
</div>
</header>
<div class="scene">
<canvas id="view" width="1100" height="595" aria-label="Side view of people riding a simulated escalator"></canvas>
</div>
<div class="foot">
<div class="status"><span id="dot" class="dot"></span><span id="status">Moving uphill</span></div>
<div>Press reverse again to restore the original direction. End stops keep the simulation on screen.</div>
</div>
</main>
<script>
(() => {
"use strict";
const canvas = document.getElementById("view");
const ctx = canvas.getContext("2d");
const reverseButton = document.getElementById("reverse");
const resetButton = document.getElementById("reset");
const status = document.getElementById("status");
const dot = document.getElementById("dot");
const LENGTH = 32;
const HALF = 0.48;
const RADIUS = 0.17;
const DT = 1 / 120;
const GRAVITY = 13;
const SLOPE = 0.30;
const COS = Math.cos(SLOPE);
const SIN = Math.sin(SLOPE);
const UP_SPEED = 0.65;
const DOWN_SPEED = -3.4;
const MOTOR_ACCEL = 55;
const COLORS = [
"#f0a985", "#85c8bd", "#e8c77e", "#9bb7df", "#d89bab",
"#b9ce91", "#dbafda", "#e3b895", "#8bc4d1"
];
let people = [];
let reversed = false;
let beltSpeed = UP_SPEED;
let beltPhase = 0;
let accumulator = 0;
let lastTime = 0;
function clamp(v, lo, hi) { return Math.max(lo, Math.min(hi, v)); }
function wrapAngle(a) {
while (a > Math.PI) a -= 2 * Math.PI;
while (a < -Math.PI) a += 2 * Math.PI;
return a;
}
function reset() {
people = [];
for (let i = 0; i < 18; i++) {
// Small physical differences break perfect symmetry; everyone uses the same rules.
const mass = 0.94 + 0.08 * Math.sin(i * 2.17);
people.push({
x: 5.0 + i * 1.14,
y: HALF + RADIUS + 0.003,
vx: UP_SPEED,
vy: 0,
angle: Math.PI / 2 + 0.012 * Math.sin(i * 1.73),
omega: 0,
mass,
invMass: 1 / mass,
inertia: mass * 0.135,
invI: 1 / (mass * 0.135),
strength: 1 + 0.07 * Math.sin(i * 1.37),
color: COLORS[i % COLORS.length]
});
}
reversed = false;
beltSpeed = UP_SPEED;
beltPhase = 0;
accumulator = 0;
updateControls();
}
function updateControls() {
reverseButton.textContent = reversed ? "Restore uphill direction" : "Reverse escalator";
reverseButton.setAttribute("aria-pressed", String(reversed));
status.textContent = reversed ? "Reverse selected · downhill" : "Moving uphill";
dot.classList.toggle("reverse", reversed);
}
reverseButton.addEventListener("click", () => {
reversed = !reversed;
updateControls();
});
resetButton.addEventListener("click", reset);
function endpoints(p) {
const dx = Math.cos(p.angle) * HALF;
const dy = Math.sin(p.angle) * HALF;
return {
ax: p.x - dx, ay: p.y - dy,
bx: p.x + dx, by: p.y + dy
};
}
// Closest points on two line segments, including their endpoints.
function closestSegments(a, b) {
const ux = a.bx - a.ax, uy = a.by - a.ay;
const vx = b.bx - b.ax, vy = b.by - b.ay;
const wx = a.ax - b.ax, wy = a.ay - b.ay;
const aa = ux * ux + uy * uy;
const bb = ux * vx + uy * vy;
const cc = vx * vx + vy * vy;
const dd = ux * wx + uy * wy;
const ee = vx * wx + vy * wy;
const denom = aa * cc - bb * bb;
let s = denom > 1e-9 ? clamp((bb * ee - cc * dd) / denom, 0, 1) : 0;
let t = (bb * s + ee) / cc;
if (t < 0) {
t = 0;
s = clamp(-dd / aa, 0, 1);
} else if (t > 1) {
t = 1;
s = clamp((bb - dd) / aa, 0, 1);
}
return {
ax: a.ax + s * ux, ay: a.ay + s * uy,
bx: b.ax + t * vx, by: b.ay + t * vy
};
}
function velocityAt(p, rx, ry) {
return { x: p.vx - p.omega * ry, y: p.vy + p.omega * rx };
}
function applyImpulse(p, ix, iy, rx, ry) {
p.vx += ix * p.invMass;
p.vy += iy * p.invMass;
p.omega += (rx * iy - ry * ix) * p.invI;
}
// Contact against an immovable surface. n points into the person.
function surfaceContact(p, px, py, nx, ny, penetration, svx, svy, friction) {
if (penetration <= 0) return;
p.x += nx * penetration * 0.62;
p.y += ny * penetration * 0.62;
const rx = px - p.x;
const ry = py - p.y;
const v = velocityAt(p, rx, ry);
const rvx = v.x - svx;
const rvy = v.y - svy;
const vn = rvx * nx + rvy * ny;
const rn = rx * ny - ry * nx;
const normalMass = p.invMass + rn * rn * p.invI;
const bias = Math.min(0.7, Math.max(0, penetration - 0.003) * 0.1 / DT);
const jn = Math.max(0, (-vn + bias) / normalMass);
applyImpulse(p, nx * jn, ny * jn, rx, ry);
const tx = -ny, ty = nx;
const after = velocityAt(p, rx, ry);
const vt = (after.x - svx) * tx + (after.y - svy) * ty;
const rt = rx * ty - ry * tx;
const tangentMass = p.invMass + rt * rt * p.invI;
const jt = clamp(-vt / tangentMass, -friction * jn, friction * jn);
applyImpulse(p, tx * jt, ty * jt, rx, ry);
}
function solveSurfaces(p) {
const e = endpoints(p);
const points = [[e.ax, e.ay], [e.bx, e.by]];
for (const q of points) {
// Moving floor and stationary end stops.
surfaceContact(p, q[0], q[1] - RADIUS, 0, 1,
RADIUS - q[1], beltSpeed, 0, 0.88);
surfaceContact(p, q[0] - RADIUS, q[1], 1, 0,
RADIUS - q[0], 0, 0, 0.45);
surfaceContact(p, q[0] + RADIUS, q[1], -1, 0,
q[0] + RADIUS - LENGTH, 0, 0, 0.45);
}
}
function solvePair(a, b) {
// A cheap broad phase before the segment calculation.
if (Math.abs(a.x - b.x) > 1.5 || Math.abs(a.y - b.y) > 1.5) return;
const c = closestSegments(endpoints(a), endpoints(b));
let dx = c.ax - c.bx;
let dy = c.ay - c.by;
let dist = Math.hypot(dx, dy);
const penetration = 2 * RADIUS - dist;
if (penetration <= 0) return;
if (dist < 1e-7) {
dx = a.x - b.x;
dy = a.y - b.y;
dist = Math.hypot(dx, dy);
if (dist < 1e-7) { dx = 1; dy = 0; dist = 1; }
}
const nx = dx / dist, ny = dy / dist;
const totalInv = a.invMass + b.invMass;
const correction = Math.max(0, penetration - 0.001) * 0.58 / totalInv;
a.x += nx * correction * a.invMass;
a.y += ny * correction * a.invMass;
b.x -= nx * correction * b.invMass;
b.y -= ny * correction * b.invMass;
const px = (c.ax + c.bx) * 0.5;
const py = (c.ay + c.by) * 0.5;
const rax = px - a.x, ray = py - a.y;
const rbx = px - b.x, rby = py - b.y;
let va = velocityAt(a, rax, ray);
let vb = velocityAt(b, rbx, rby);
const vn = (va.x - vb.x) * nx + (va.y - vb.y) * ny;
const ran = rax * ny - ray * nx;
const rbn = rbx * ny - rby * nx;
const normalMass = totalInv + ran * ran * a.invI + rbn * rbn * b.invI;
const bias = Math.min(0.7, Math.max(0, penetration - 0.003) * 0.1 / DT);
const jn = Math.max(0, (-vn + bias) / normalMass);
applyImpulse(a, nx * jn, ny * jn, rax, ray);
applyImpulse(b, -nx * jn, -ny * jn, rbx, rby);
const tx = -ny, ty = nx;
va = velocityAt(a, rax, ray);
vb = velocityAt(b, rbx, rby);
const vt = (va.x - vb.x) * tx + (va.y - vb.y) * ty;
const rat = rax * ty - ray * tx;
const rbt = rbx * ty - rby * tx;
const tangentMass = totalInv + rat * rat * a.invI + rbt * rbt * b.invI;
const jt = clamp(-vt / tangentMass, -0.48 * jn, 0.48 * jn);
applyImpulse(a, tx * jt, ty * jt, rax, ray);
applyImpulse(b, -tx * jt, -ty * jt, rbx, rby);
}
function simulate() {
const target = reversed ? DOWN_SPEED : UP_SPEED;
beltSpeed += clamp(target - beltSpeed, -MOTOR_ACCEL * DT, MOTOR_ACCEL * DT);
beltPhase += beltSpeed * DT;
for (const p of people) {
const tilt = wrapAngle(Math.PI / 2 - p.angle);
const bottom = p.y - Math.abs(Math.sin(p.angle)) * HALF - RADIUS;
const grounded = bottom < 0.12;
let crowd = 0;
if (Math.abs(tilt) > 0.8) {
for (const other of people) {
if (other !== p && Math.hypot(p.x - other.x, p.y - other.y) < 0.82) crowd++;
}
}
// A balance motor, not an animation: it acts through the same rigid body
// and is limited by its torque and by the contact solver.
if (grounded) {
const gettingUp = Math.abs(tilt) > 0.8;
const maxTorque = (gettingUp ? 2.7 / (1 + 0.5 * crowd) : 3.5) * p.strength;
const desired = (gettingUp ? 5.5 : 14) * tilt - 1.4 * p.omega;
p.omega += clamp(desired, -maxTorque, maxTorque) * p.invI * DT;
}
p.vx += -GRAVITY * SIN * DT;
p.vy += -GRAVITY * COS * DT;
p.omega *= 0.999;
p.omega = clamp(p.omega, -12, 12);
p.x += p.vx * DT;
p.y += p.vy * DT;
p.angle = wrapAngle(p.angle + p.omega * DT);
}
for (let iteration = 0; iteration < 4; iteration++) {
for (const p of people) solveSurfaces(p);
for (let i = 0; i < people.length; i++) {
for (let j = i + 1; j < people.length; j++) {
solvePair(people[i], people[j]);
}
}
}
}
function resize() {
const rect = canvas.getBoundingClientRect();
const dpr = Math.min(window.devicePixelRatio || 1, 2);
const w = Math.max(1, Math.round(rect.width * dpr));
const h = Math.max(1, Math.round(rect.height * dpr));
if (canvas.width !== w || canvas.height !== h) {
canvas.width = w;
canvas.height = h;
}
}
function draw() {
resize();
const w = canvas.width, h = canvas.height;
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.fillStyle = "#17262b";
ctx.fillRect(0, 0, w, h);
// World x runs up the incline; world y is perpendicular to the belt.
const scale = Math.min(w / 35.4, h / 16.3);
const originX = w * 0.065;
const originY = h * 0.84;
ctx.setTransform(
scale * COS, -scale * SIN,
-scale * SIN, -scale * COS,
originX, originY
);
// Distant guide rails, behind the riders.
ctx.lineCap = "round";
ctx.strokeStyle = "#58716e";
ctx.lineWidth = 0.09;
ctx.beginPath();
ctx.moveTo(0, 2.42);
ctx.lineTo(LENGTH, 2.42);
ctx.stroke();
ctx.strokeStyle = "#3b5553";
ctx.lineWidth = 0.045;
for (let x = 0.7; x < LENGTH; x += 2) {
ctx.beginPath();
ctx.moveTo(x, 0.04);
ctx.lineTo(x, 2.42);
ctx.stroke();
}
// Conveyor housing.
ctx.fillStyle = "#30464a";
ctx.beginPath();
ctx.moveTo(0, -1.05);
ctx.lineTo(LENGTH, -1.05);
ctx.lineTo(LENGTH, 0);
ctx.lineTo(0, 0);
ctx.closePath();
ctx.fill();
ctx.strokeStyle = "#72908a";
ctx.lineWidth = 0.075;
ctx.stroke();
ctx.fillStyle = "#1d3034";
ctx.fillRect(0, -0.76, LENGTH, 0.35);
ctx.strokeStyle = "#9cb3a4";
ctx.lineWidth = 0.035;
const spacing = 0.58;
const offset = ((beltPhase % spacing) + spacing) % spacing;
for (let x = offset; x < LENGTH; x += spacing) {
ctx.beginPath();
ctx.moveTo(x, -0.03);
ctx.lineTo(x + 0.18, -0.36);
ctx.stroke();
}
ctx.strokeStyle = "#d3c47b";
ctx.lineWidth = 0.055;
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(LENGTH, 0);
ctx.stroke();
// Visible end stops correspond to the simulated collision boundaries.
ctx.strokeStyle = "#d6a66b";
ctx.lineWidth = 0.13;
ctx.beginPath();
ctx.moveTo(0, -0.1); ctx.lineTo(0, 1.15);
ctx.moveTo(LENGTH, -0.1); ctx.lineTo(LENGTH, 1.15);
ctx.stroke();
// Draw the body in the capsule's own orientation. Limbs are decorative;
// the capsule itself is what collides.
for (const p of people) {
ctx.save();
ctx.translate(p.x, p.y);
ctx.rotate(p.angle - Math.PI / 2);
const tilt = wrapAngle(Math.PI / 2 - p.angle);
const arm = clamp(tilt * 0.35 + p.omega * 0.035, -0.35, 0.35);
ctx.lineCap = "round";
ctx.lineJoin = "round";
ctx.strokeStyle = "#102126";
ctx.lineWidth = 0.14;
ctx.beginPath();
ctx.moveTo(0, -0.12);
ctx.lineTo(-0.12, -0.48);
ctx.moveTo(0, -0.12);
ctx.lineTo(0.12, -0.48);
ctx.stroke();
ctx.strokeStyle = p.color;
ctx.lineWidth = 0.24;
ctx.beginPath();
ctx.moveTo(0, -0.16);
ctx.lineTo(0, 0.25);
ctx.stroke();
ctx.lineWidth = 0.075;
ctx.beginPath();
ctx.moveTo(-0.02, 0.18);
ctx.lineTo(-0.23 - arm, -0.04);
ctx.moveTo(0.02, 0.18);
ctx.lineTo(0.23 - arm, -0.04);
ctx.stroke();
ctx.fillStyle = "#f0cfb7";
ctx.beginPath();
ctx.arc(0, 0.46, 0.145, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = "#27383a";
ctx.beginPath();
ctx.arc(0, 0.51, 0.145, 0, Math.PI);
ctx.fill();
ctx.restore();
}
}
function frame(time) {
if (!lastTime) lastTime = time;
const elapsed = Math.min((time - lastTime) / 1000, 0.05);
lastTime = time;
accumulator += elapsed;
while (accumulator >= DT) {
simulate();
accumulator -= DT;
}
draw();
requestAnimationFrame(frame);
}
reset();
requestAnimationFrame(frame);
})();
</script>
</body>
</html>
The belt changes speed rapidly; friction at the feet tips riders, and their capsule contacts can cause sliding, collisions and piles. Restoring the direction can give them room to recover.
The nudges are limited balancing/get-up torque, small differences in mass and strength, and solid end stops. The figures have decorative limbs rather than articulated joints, the collision shapes are simple capsules, and crowded riders can remain jammed rather than recovering gracefully. The finite end stops can also cause a pile if you leave the escalator running long enough without reversing it.