47:51Ranking major U.S. Open-Weights Models. (Where does Ripple 65B fit in?)
Nov 6, 2026 · YouTube
38:58Testing Particle-1: a post-trained DeepSeek V4.1 Flash specializing in coding and efficiency
Oct 28, 2026 · YouTube
29:55Kimi K3.1 Pro Looked Incredible. Then I Kept Testing.
Oct 26, 2026 · YouTube
22:47Mistral Large 4 Is Cheap. But Is It Good Enough to Compete?
Oct 6, 2026 · YouTube
16:54Stealth Union Alpha: Is this another cheap GPT model? (better than Luna, cheaper than Terra?)
Sep 16, 2026 · YouTube
16:45DeepSeek V4.1 Flash Is Shockingly Good for the Price
Sep 10, 2026 · YouTube
17:14How much better is GPT-6 Astra over Claude Opus 5? Real Website and Coding Tests
Sep 5, 2026 · YouTube
29:13Implementing a multiplayer game with GPT-6 Astra (+ Gemini 3.8, Opus 5, Sol Pro)
Sep 5, 2026 · YouTube
19:42Meta Muse Spark 1.3 Review: A Huge Leap Forward
Sep 2, 2026 · YouTube
17:31Testing Gemini 3.8 Flash on Real Coding Challenges (vs. other models)
Sep 2, 2026 · YouTube
You are a graphics engineer who has spent a lot of time finding out why things are slow.
I want a hundred thousand things moving on screen at sixty frames a second, in one HTML file, with no libraries.
data: URIs are fine.localStorage, sessionStorage, cookies or indexedDB — sandboxed frame, they throw.html block.One paragraph, before the file. Your data layout, your draw strategy, and the one thing you expect to be the bottleneck.
The single fenced html block.
This is the section I care most about. Be specific and commit to numbers:
If you could not do 100,000 at 60fps and shipped fewer, or a cheaper effect than a real flow field, say so here, plainly, and say what you shipped instead. Write Nothing dropped. if you delivered the brief in full.
I am going to open the file and read the FPS counter.
Then I am going to compare it against your prediction. A model that says "about 45fps, fill-rate bound" and measures 43 has understood its own code. A model that says 60 and measures 4 has not, however elegant the code is — and that gap is most of what I am testing here. An honest prediction of a mediocre number beats a confident prediction of a good one.
Positions and velocities use four structure-of-arrays Float32Arrays. An analytic, time-varying curl field is evaluated on a 76 × 51 grid each frame and bilinearly sampled by every particle. Drawing is one pixel per particle into a Uint32Array-backed ImageData; only the previous frame’s head pixels are cleared, followed by one putImageData and one composited blit over a fading canvas to produce trails. I expect the single-threaded particle update—particularly field sampling and state memory traffic—to be the bottleneck, not canvas rasterization.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>100,000 particles — evolving curl field</title>
<style>
html, body {
margin: 0;
min-height: 100%;
background: #02040a;
}
body {
min-height: 100vh;
display: grid;
place-items: center;
overflow: auto;
color: #d5e6ed;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.stage {
position: relative;
width: 900px;
height: 600px;
flex: none;
box-shadow: 0 0 60px #000;
}
canvas {
display: block;
width: 900px;
height: 600px;
background: #030712;
}
.hud {
position: absolute;
top: 12px;
left: 12px;
display: flex;
gap: 18px;
padding: 8px 11px;
background: rgba(2, 6, 14, 0.76);
border: 1px solid rgba(139, 194, 210, 0.18);
border-radius: 4px;
pointer-events: none;
font-size: 12px;
line-height: 1;
letter-spacing: 0.02em;
font-variant-numeric: tabular-nums;
}
.hud span { color: #7895a3; }
.hud b { color: #d9f2f2; font-weight: 600; }
</style>
</head>
<body>
<div class="stage">
<canvas id="view" width="900" height="600"></canvas>
<div class="hud">
<span>FPS <b id="fps">—</b></span>
<span>frame <b id="frameTime">—</b></span>
<span>particles <b id="count">—</b></span>
</div>
</div>
<script>
(() => {
'use strict';
const W = 900;
const H = 600;
const CELL = 12;
const GW = W / CELL + 1;
const GH = H / CELL + 1;
const canvas = document.getElementById('view');
const fpsNode = document.getElementById('fps');
const frameTimeNode = document.getElementById('frameTime');
const countNode = document.getElementById('count');
const main = canvas.getContext('2d', { alpha: false });
main.imageSmoothingEnabled = false;
main.fillStyle = '#030712';
main.fillRect(0, 0, W, H);
// SoA state: 1.6 MB for all 100,000 particles.
const xs = new Float32Array(100000);
const ys = new Float32Array(xs.length);
const vxs = new Float32Array(xs.length);
const vys = new Float32Array(xs.length);
const n = xs.length;
for (let i = 0; i < n; i++) {
xs[i] = Math.random() * W;
ys[i] = Math.random() * H;
vxs[i] = (Math.random() - 0.5) * 16;
vys[i] = (Math.random() - 0.5) * 16;
}
// The field is sampled bilinearly; its analytic source evolves every frame.
const fieldU = new Float32Array(GW * GH);
const fieldV = new Float32Array(GW * GH);
function rebuildField(t) {
const driftX = 11 + 4 * Math.sin(t * 0.07);
const driftY = 2 * Math.cos(t * 0.09);
let p = 0;
for (let gy = 0; gy < GH; gy++) {
const y = gy * CELL;
const phase1y = y * 0.013 + t * 0.21;
const phase2y = y * 0.009 - t * 0.16;
const phase3y = -y * 0.019 + t * 0.11;
for (let gx = 0; gx < GW; gx++, p++) {
const x = gx * CELL;
/*
These components are the curl of a sum of three traveling
stream-function waves, plus uniform drift. Consequently the
waves produce actual eddies rather than independently wobbling
direction angles, and the wave portion is divergence-free.
*/
const c1 = Math.cos(x * 0.0105 + phase1y);
const c2 = Math.cos(phase2y - x * 0.016);
const s3 = Math.sin(x * 0.024 + phase3y);
fieldU[p] = driftX + 44.2 * c1 + 19.8 * c2 + 20.9 * s3;
fieldV[p] = driftY - 35.7 * c1 + 35.2 * c2 + 26.4 * s3;
}
}
}
// Transparent canvas containing only this frame's particle heads.
const layer = document.createElement('canvas');
layer.width = W;
layer.height = H;
const layerContext = layer.getContext('2d', { alpha: true });
const headImage = layerContext.createImageData(W, H);
const pixels = new Uint32Array(headImage.data.buffer);
// Previous head locations let us avoid clearing all 540,000 pixels in JS.
const drawn = new Uint32Array(n);
let headsLive = false;
function pack(r, g, b) {
return ((255 << 24) | (b << 16) | (g << 8) | r) >>> 0;
}
// ABGR packing on the little-endian platforms targeted by Chrome.
const palette = new Uint32Array([
pack(60, 126, 181),
pack(58, 164, 179),
pack(81, 188, 192),
pack(102, 153, 207),
pack(47, 145, 151),
pack(128, 196, 199),
pack(76, 112, 171),
pack(155, 177, 192)
]);
countNode.textContent = xs.length.toLocaleString('en-US');
// Honest FPS: mean requestAnimationFrame interval over the last 120 frames.
const frameSamples = new Float32Array(120);
let sampleCursor = 0;
let sampleCount = 0;
let lastStamp = 0;
let lastHudUpdate = 0;
let simulationTime = 0;
function frame(now) {
let dt;
if (lastStamp === 0) {
dt = 1 / 60;
} else {
const elapsed = now - lastStamp;
frameSamples[sampleCursor] = elapsed;
sampleCursor = (sampleCursor + 1) % frameSamples.length;
if (sampleCount < frameSamples.length) sampleCount++;
// Simulation stability after a stall; FPS still records the real stall.
dt = Math.min(0.05, Math.max(0.001, elapsed / 1000));
}
lastStamp = now;
simulationTime += dt;
if (headsLive) {
for (let i = 0; i < n; i++) {
pixels[drawn[i]] = 0;
}
}
rebuildField(simulationTime);
const follow = 1 - Math.exp(-dt * 3.2);
for (let i = 0; i < n; i++) {
let px = xs[i];
let py = ys[i];
const gx = px / CELL;
const gy = py / CELL;
const ix = gx | 0;
const iy = gy | 0;
const fx = gx - ix;
const fy = gy - iy;
const a = iy * GW + ix;
const b = a + GW;
const u00 = fieldU[a];
const u10 = fieldU[a + 1];
const u01 = fieldU[b];
const u11 = fieldU[b + 1];
const v00 = fieldV[a];
const v10 = fieldV[a + 1];
const v01 = fieldV[b];
const v11 = fieldV[b + 1];
const ut = u00 + (u10 - u00) * fx;
const ub = u01 + (u11 - u01) * fx;
const u = ut + (ub - ut) * fy;
const vt = v00 + (v10 - v00) * fx;
const vb = v01 + (v11 - v01) * fx;
const v = vt + (vb - vt) * fy;
// Every particle retains and integrates its own velocity.
let vx = vxs[i];
let vy = vys[i];
vx += (u - vx) * follow;
vy += (v - vy) * follow;
px += vx * dt;
py += vy * dt;
// Toroidal recycling: no particle is removed or left undrawn.
if (px < 0) px += W;
else if (px >= W) px -= W;
if (py < 0) py += H;
else if (py >= H) py -= H;
xs[i] = px;
ys[i] = py;
vxs[i] = vx;
vys[i] = vy;
const pixelIndex = (py | 0) * W + (px | 0);
pixels[pixelIndex] = palette[i & 7];
drawn[i] = pixelIndex;
}
headsLive = true;
layerContext.putImageData(headImage, 0, 0);
// Fade old trails in canvas-compositing hardware, then add the new heads.
main.globalCompositeOperation = 'source-over';
main.globalAlpha = 1 - Math.exp(-dt * 5.0);
main.fillStyle = '#030712';
main.fillRect(0, 0, W, H);
main.globalAlpha = 1;
main.globalCompositeOperation = 'lighter';
main.drawImage(layer, 0, 0);
if (sampleCount && now - lastHudUpdate > 350) {
let total = 0;
for (let i = 0; i < sampleCount; i++) {
total += frameSamples[i];
}
const averageMs = total / sampleCount;
fpsNode.textContent = (1000 / averageMs).toFixed(1);
frameTimeNode.textContent = averageMs.toFixed(1) + ' ms';
countNode.textContent = xs.length.toLocaleString('en-US');
lastHudUpdate = now;
}
requestAnimationFrame(frame);
}
document.addEventListener('visibilitychange', () => {
if (!document.hidden) {
lastStamp = 0;
sampleCursor = 0;
sampleCount = 0;
}
});
requestAnimationFrame(frame);
})();
</script>
</body>
</html>
ImageData, and compositing two full-canvas operations.gl.POINTS draw without CPU readback. Trails would remain in GPU framebuffers rather than round-tripping through ImageData.Nothing dropped.