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
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
You write crowd simulations where the behavior emerges from rules each person follows.
Build me a single HTML page: a section of a football stadium emptying through one exit gate.
#urgency=0.8&gate=1.2&column=1 starts simulation at load with those settings: urgency 0.0 to 1.0, gate width in yards, column 0 or 1. Any values work.data: URIs are fine.localStorage, sessionStorage, cookies or indexedDB — sandboxed frame, they throw.html block.Before the file, two quantitative predictions: whether increasing urgency from 0.3 to 0.9 increases or decreases evacuation time at gate=1.2 and column=0, with the predicted difference in seconds; and whether enabling the column (column=1) increases or decreases evacuation time at urgency=0.9 and gate=1.2, with the predicted difference in seconds.
The single fenced html block.
Where your crowd behaves like a real one and where it does not. Name anything you special-cased: a nudge that breaks a jam, a speed cap at the gate, an allowance for overlap. If your page never jams at the gate, say so.
These are rough, untested estimates for the page below:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>The Last Gate</title>
<style>
:root {
color-scheme: dark;
--ink: #eef1e9;
--muted: #9daaa3;
--panel: #192420;
--line: #34443c;
--lime: #d2ee83;
--orange: #f1aa75;
}
* { box-sizing: border-box; }
body {
margin: 0;
background: #101815;
color: var(--ink);
font: 15px/1.4 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
main { max-width: 1500px; margin: auto; padding: 24px; }
header {
display: flex; justify-content: space-between; align-items: end;
gap: 20px; margin-bottom: 20px;
}
.eyebrow {
color: var(--lime); font-size: 11px; font-weight: 800;
letter-spacing: .17em; text-transform: uppercase;
}
h1 { margin: 4px 0 0; font-size: clamp(28px, 4vw, 46px); line-height: 1; letter-spacing: -.045em; }
.intro { max-width: 420px; margin: 0; color: var(--muted); font-size: 13px; }
.layout { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: 16px; }
.stage, aside {
border: 1px solid var(--line); border-radius: 12px;
background: var(--panel); overflow: hidden;
}
.stage { position: relative; }
.stage-top {
display: flex; justify-content: space-between; gap: 10px;
padding: 13px 17px; color: var(--muted);
text-transform: uppercase; font-size: 10px; font-weight: 800; letter-spacing: .13em;
}
canvas { display: block; width: 100%; height: auto; background: #273a30; }
.stage-bottom {
display: flex; flex-wrap: wrap; gap: 18px; padding: 12px 17px;
color: var(--muted); font-size: 12px;
}
.dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; margin-right: 6px; }
aside { padding: 19px; align-self: start; }
.section-title {
margin: 0 0 14px; color: var(--muted); font-size: 10px;
font-weight: 800; letter-spacing: .14em; text-transform: uppercase;
}
.metrics { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 22px; }
.metric { min-width: 0; padding: 12px; border: 1px solid var(--line); border-radius: 8px; background: #14201a; }
.metric strong { display: block; font-size: 25px; line-height: 1.15; font-variant-numeric: tabular-nums; }
.metric span { display: block; margin-top: 5px; color: var(--muted); font-size: 11px; }
.metric.wide { grid-column: 1 / -1; }
.metric.wide strong { color: var(--lime); }
.control { margin: 0 0 19px; }
.control label, .control .label {
display: flex; justify-content: space-between; align-items: center;
gap: 10px; font-weight: 700; font-size: 13px;
}
output { color: var(--lime); font-variant-numeric: tabular-nums; }
input[type="range"] { width: 100%; margin: 11px 0 0; accent-color: var(--lime); }
input[type="number"], select {
width: 100%; margin-top: 8px; padding: 9px 10px;
border: 1px solid #52665a; border-radius: 6px;
color: var(--ink); background: #101a15; font: inherit;
}
.hint { margin: 5px 0 0; color: var(--muted); font-size: 11px; }
.check {
display: flex; justify-content: space-between; align-items: center;
gap: 10px; font-weight: 700; font-size: 13px;
}
input[type="checkbox"] { width: 19px; height: 19px; accent-color: var(--lime); }
.buttons { display: flex; gap: 8px; }
button {
flex: 1; padding: 10px; border: 1px solid #617966; border-radius: 6px;
background: transparent; color: var(--ink); font: inherit; font-weight: 700; cursor: pointer;
}
button.primary { background: var(--lime); border-color: var(--lime); color: #172014; }
button:hover { filter: brightness(1.12); }
.status {
min-height: 34px; margin: 14px 0 0; color: var(--orange); font-size: 12px;
}
footer { margin-top: 14px; color: var(--muted); font-size: 12px; }
@media (max-width: 800px) {
main { padding: 14px; }
header { align-items: start; flex-direction: column; }
.layout { grid-template-columns: 1fr; }
aside { order: -1; }
.metrics { grid-template-columns: repeat(3, 1fr); }
.metric.wide { grid-column: auto; }
}
</style>
</head>
<body>
<main>
<header>
<div><div class="eyebrow">A small crowd experiment</div><h1>The last gate.</h1></div>
<p class="intro">A section of supporters, one way out. Change the conditions and watch the crowd negotiate the opening.</p>
</header>
<div class="layout">
<section class="stage" aria-label="Top-down stadium simulation">
<div class="stage-top"><span>Section 08 · top view</span><span>Exit ↓</span></div>
<canvas id="view" width="1100" height="945" aria-label="People moving from the stands toward a gate"></canvas>
<div class="stage-bottom">
<span><i class="dot" style="background:#d2ee83"></i>Supporters</span>
<span><i class="dot" style="background:#f1aa75"></i>Gate</span>
<span><i class="dot" style="background:#a6b4a9"></i>Column</span>
</div>
</section>
<aside>
<p class="section-title">Live measurements</p>
<div class="metrics">
<div class="metric"><strong id="remaining">220</strong><span>people inside</span></div>
<div class="metric"><strong id="rate">0</strong><span>exits / minute</span></div>
<div class="metric wide"><strong id="clock">0:00.0</strong><span id="clockLabel">elapsed simulation time</span></div>
</div>
<p class="section-title">Conditions</p>
<div class="control">
<label for="urgency">Urgency <output id="urgencyValue">0.50</output></label>
<input id="urgency" type="range" min="0" max="1" step="0.01" value="0.5">
<p class="hint">Changes desired speed and contact force while running.</p>
</div>
<div class="control">
<label for="gate">Gate width <span>yards</span></label>
<input id="gate" type="number" min="0" max="20" step="0.1" value="1.2">
<p class="hint">0–20 yards. Changing the gate refills the section.</p>
</div>
<div class="control">
<label class="check" for="column"><span>Column in front of gate</span><input id="column" type="checkbox"></label>
<p class="hint">Changing the column refills the section.</p>
</div>
<div class="control">
<label for="speed">Playback speed</label>
<select id="speed">
<option value="1">1×</option>
<option value="2">2×</option>
<option value="4">4×</option>
</select>
<p class="hint">The timer and flow rate use simulation time.</p>
</div>
<div class="buttons">
<button id="pause" type="button">Pause</button>
<button id="reset" class="primary" type="button">Reset</button>
</div>
<p class="status" id="status" role="status"></p>
</aside>
</div>
<footer>Flow is measured from actual exits over the last 30 seconds, or the elapsed time if shorter. Geometry changes start a new run.</footer>
</main>
<script>
(() => {
"use strict";
// World dimensions are metres; the gate control is converted from yards.
const W = 22, H = 18.9, WALL = 16.5, R = 0.21;
const COL_R = 0.32, COL_X = W / 2, COL_Y = WALL - 1.35;
const COUNT = 220, DT = 1 / 30;
const canvas = document.getElementById("view");
const ctx = canvas.getContext("2d");
const ui = {
urgency: document.getElementById("urgency"),
urgencyValue: document.getElementById("urgencyValue"),
gate: document.getElementById("gate"),
column: document.getElementById("column"),
speed: document.getElementById("speed"),
pause: document.getElementById("pause"),
reset: document.getElementById("reset"),
remaining: document.getElementById("remaining"),
rate: document.getElementById("rate"),
clock: document.getElementById("clock"),
clockLabel: document.getElementById("clockLabel"),
status: document.getElementById("status")
};
const clamp = (v, a, b) => Math.max(a, Math.min(b, v));
const params = new URLSearchParams(location.hash.slice(1));
function hashNumber(key, fallback) {
if (!params.has(key) || params.get(key).trim() === "") return fallback;
const n = Number(params.get(key));
return Number.isFinite(n) ? n : fallback;
}
ui.urgency.value = clamp(hashNumber("urgency", 0.5), 0, 1);
ui.gate.value = clamp(hashNumber("gate", 1.2), 0, 20);
ui.column.checked = hashNumber("column", 0) === 1;
let people = [], exits = [], elapsed = 0, running = true, complete = false;
let gateWidth = 0, left = 0, right = 0, hasColumn = false;
let accumulator = 0, lastFrame = 0, stepNumber = 0;
function syncUrgency() {
ui.urgencyValue.textContent = Number(ui.urgency.value).toFixed(2);
}
function reset() {
let yards = Number(ui.gate.value);
if (!Number.isFinite(yards)) yards = 1.2;
yards = clamp(yards, 0, 20);
ui.gate.value = String(yards);
gateWidth = yards * 0.9144;
left = W / 2 - gateWidth / 2;
right = W / 2 + gateWidth / 2;
hasColumn = ui.column.checked;
people = [];
// A spaced rectangular seating section, with no initial overlaps.
for (let row = 0; row < 10; row++) {
for (let col = 0; col < 22; col++) {
const id = row * 22 + col;
people.push({
id, x: W / 2 + (col - 10.5) * 0.67,
y: 1.55 + row * 0.67, vx: 0, vy: 0, ax: 0, ay: 0
});
}
}
exits = [];
elapsed = 0;
accumulator = 0;
stepNumber = 0;
complete = false;
running = true;
ui.pause.textContent = "Pause";
updateUI();
draw();
}
// A circle must not intersect either solid segment of the gate wall.
function wallSegmentClear(x, y, x1, x2) {
const nearX = clamp(x, x1, x2);
const dx = x - nearX, dy = y - WALL;
return dx * dx + dy * dy >= R * R;
}
function validPosition(x, y, self) {
if (x < R || x > W - R || y < R) return false;
if (!wallSegmentClear(x, y, 0, left) ||
!wallSegmentClear(x, y, right, W)) return false;
if (hasColumn) {
const dx = x - COL_X, dy = y - COL_Y;
if (dx * dx + dy * dy < (R + COL_R) * (R + COL_R)) return false;
}
const diameter2 = 4 * R * R;
for (let j = 0; j < people.length; j++) {
const other = people[j];
if (other === self) continue;
const dx = x - other.x, dy = y - other.y;
if (dx * dx + dy * dy < diameter2) return false;
}
return true;
}
function advancePerson(p) {
const startX = p.x, startY = p.y;
// Small increments prevent passing through a wall or another person.
// If a diagonal is blocked, the axis attempts provide wall sliding.
const dx = p.vx * DT / 2, dy = p.vy * DT / 2;
for (let k = 0; k < 2; k++) {
if (validPosition(p.x + dx, p.y + dy, p)) {
p.x += dx; p.y += dy;
} else {
if (validPosition(p.x + dx, p.y, p)) p.x += dx;
if (validPosition(p.x, p.y + dy, p)) p.y += dy;
}
}
// Blocked motion transfers back into velocity instead of accumulating
// unlimited momentum against an immovable boundary.
p.vx = (p.x - startX) / DT;
p.vy = (p.y - startY) / DT;
}
function tick() {
const urgency = Number(ui.urgency.value);
const desiredSpeed = 0.85 + 1.45 * urgency;
const maxSpeed = desiredSpeed * 1.25;
for (const p of people) {
// Aim for the accessible span rather than a single point at the gate.
const clearance = R + 0.025;
let targetX = W / 2;
if (gateWidth > 2 * clearance) {
targetX = clamp(p.x, left + clearance, right - clearance);
}
let targetY = WALL + 1;
// The same obstacle-avoidance rule is used by everyone.
// Select the nearer side of the column before heading back to the gate.
if (hasColumn && p.y < COL_Y + 0.2 &&
p.y > COL_Y - 3.0 && Math.abs(p.x - COL_X) < 1.5) {
let side = Math.sign(p.x - COL_X);
if (!side) side = p.id % 2 ? 1 : -1;
targetX = COL_X + side * (COL_R + R + 0.18);
targetY = COL_Y + 0.45;
}
const dx = targetX - p.x, dy = targetY - p.y;
const len = Math.hypot(dx, dy) || 1;
p.ax = (desiredSpeed * dx / len - p.vx) / 0.48;
p.ay = (desiredSpeed * dy / len - p.vy) / 0.48;
}
// Equal and opposite short-range contact / personal-space forces.
// Hard exclusion is enforced separately during movement.
const reach = 0.65;
const contactStrength = 5 + 9 * urgency;
for (let i = 0; i < people.length; i++) {
const a = people[i];
for (let j = i + 1; j < people.length; j++) {
const b = people[j];
const dx = a.x - b.x, dy = a.y - b.y;
const d2 = dx * dx + dy * dy;
if (d2 >= reach * reach || d2 === 0) continue;
const d = Math.sqrt(d2);
const force = (reach - d) * contactStrength;
const fx = dx / d * force, fy = dy / d * force;
a.ax += fx; a.ay += fy;
b.ax -= fx; b.ay -= fy;
}
}
for (const p of people) {
p.vx += p.ax * DT;
p.vy += p.ay * DT;
const speed = Math.hypot(p.vx, p.vy);
if (speed > maxSpeed) {
p.vx *= maxSpeed / speed;
p.vy *= maxSpeed / speed;
}
}
// Alternate traversal direction to reduce a permanent ordering bias.
if (stepNumber % 2 === 0) {
for (let i = 0; i < people.length; i++) advancePerson(people[i]);
} else {
for (let i = people.length - 1; i >= 0; i--) advancePerson(people[i]);
}
stepNumber++;
elapsed += DT;
// An exit is counted only once the whole disc is beyond the wall.
for (let i = people.length - 1; i >= 0; i--) {
if (people[i].y >= WALL + R) {
exits.push(elapsed);
people.splice(i, 1);
}
}
while (exits.length && exits[0] < elapsed - 30) exits.shift();
if (people.length === 0) {
complete = true;
running = false;
ui.pause.textContent = "Run";
}
}
function timeString(seconds) {
const minutes = Math.floor(seconds / 60);
const rest = (seconds - minutes * 60).toFixed(1).padStart(4, "0");
return minutes + ":" + rest;
}
function updateUI() {
ui.remaining.textContent = people.length;
const windowLength = Math.min(30, elapsed);
ui.rate.textContent = windowLength > 0 ? (exits.length * 60 / windowLength).toFixed(1) : "0.0";
ui.clock.textContent = timeString(elapsed);
ui.clockLabel.textContent = complete ? "time to empty" : "elapsed simulation time";
if (complete) {
ui.status.textContent = "Section empty.";
} else if (gateWidth <= 2 * R) {
ui.status.textContent = "This opening is too narrow for a person to pass.";
} else if (!running) {
ui.status.textContent = "Paused.";
} else {
ui.status.textContent = "Simulation running.";
}
}
function draw() {
const sx = canvas.width / W, sy = canvas.height / H;
ctx.setTransform(sx, 0, 0, sy, 0, 0);
// Field beyond the gate.
ctx.fillStyle = "#29483a";
ctx.fillRect(0, 0, W, H);
ctx.fillStyle = "#344b3e";
ctx.fillRect(0, 0, W, WALL);
ctx.fillStyle = "#3b5145";
ctx.fillRect(0, 0.6, W, 7.7);
// Broad stand rows, purely decorative.
for (let y = 1.15; y < 8.1; y += 0.67) {
ctx.fillStyle = "rgba(12, 27, 19, 0.22)";
ctx.fillRect(0.35, y, W - 0.7, 0.08);
}
ctx.fillStyle = "rgba(226, 239, 207, 0.10)";
ctx.font = "bold 0.29px system-ui";
ctx.fillText("STANDS", 0.65, 0.62);
ctx.fillText("CONCOURSE", 0.65, 10.2);
ctx.fillText("EXIT", W / 2 - 0.3, 18.1);
// Wall and highlighted opening.
ctx.lineCap = "butt";
ctx.lineWidth = 0.18;
ctx.strokeStyle = "#b5c4b5";
ctx.beginPath();
ctx.moveTo(0, WALL); ctx.lineTo(left, WALL);
ctx.moveTo(right, WALL); ctx.lineTo(W, WALL);
ctx.stroke();
if (gateWidth > 0) {
ctx.strokeStyle = "#f1aa75";
ctx.lineWidth = 0.08;
ctx.beginPath(); ctx.moveTo(left, WALL); ctx.lineTo(right, WALL); ctx.stroke();
}
if (hasColumn) {
ctx.fillStyle = "#a6b4a9";
ctx.beginPath(); ctx.arc(COL_X, COL_Y, COL_R, 0, Math.PI * 2); ctx.fill();
ctx.strokeStyle = "#d6dfd5"; ctx.lineWidth = 0.035; ctx.stroke();
}
for (const p of people) {
ctx.fillStyle = p.id % 5 === 0 ? "#f1c799" : (p.id % 3 === 0 ? "#b4d9a1" : "#d2ee83");
ctx.beginPath(); ctx.arc(p.x, p.y, R, 0, Math.PI * 2); ctx.fill();
ctx.strokeStyle = "rgba(19, 38, 25, 0.75)";
ctx.lineWidth = 0.035; ctx.stroke();
}
ctx.setTransform(1, 0, 0, 1, 0, 0);
}
ui.urgency.addEventListener("input", syncUrgency);
ui.gate.addEventListener("change", reset);
ui.column.addEventListener("change", reset);
ui.reset.addEventListener("click", reset);
ui.pause.addEventListener("click", () => {
if (complete) return;
running = !running;
accumulator = 0;
ui.pause.textContent = running ? "Pause" : "Run";
updateUI();
});
function frame(now) {
if (!lastFrame) lastFrame = now;
const realDelta = Math.min((now - lastFrame) / 1000, 0.15);
lastFrame = now;
if (running && !complete) {
accumulator += realDelta * Number(ui.speed.value);
// Limit work per frame. If the device cannot keep up, simulation time
// slows rather than skipping physics steps.
let steps = 0;
while (accumulator >= DT && steps < 12 && running) {
tick();
accumulator -= DT;
steps++;
}
accumulator = Math.min(accumulator, DT * 12);
}
updateUI();
draw();
requestAnimationFrame(frame);
}
syncUrgency();
reset();
requestAnimationFrame(frame);
})();
</script>
</body>
</html>
People share steering and short-range repulsion rules, press against hard boundaries, and can form queues. The displayed flow counts people who actually pass the wall.
This is not a validated crowd model: people cannot fall, change behaviour, or exert realistic sustained pressure. The column has an explicit side-selection rule; blocked movement can slide along axes. There is a general speed cap, but no gate-specific cap or anti-jam nudge. Hard position checks do not intentionally permit overlap.
I have not verified whether a permanent gate jam occurs; the numerical predictions above are estimates, not measured results.