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.
I'm going to use raw Canvas 2D with a single fillRect per particle — no paths, no arcs, no shadowBlur, no alpha compositing tricks. The data layout is a flat Float32Array of length 400,000 (x, y, vx, vy per particle, interleaved) so the update loop is a tight, branch-predictable scan with zero allocation and zero object churn. The flow field is a cheap curl-noise approximation: a sum of a few rotating sine/cosine terms evaluated per particle per frame — smooth, time-evolving, eddy-like, but O(1) per particle with no texture lookups. Draw strategy: one fillStyle set once, then 100k fillRect(x, y, 1.6, 1.6) calls with a slight trail effect via a translucent black overlay rect each frame (which gives the ink-in-water persistence). The bottleneck I expect is the 100k individual fillRect draw calls hitting the Canvas 2D rasterizer — that's the classic wall, and I'm predicting it dominates over the JS update loop.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>100k flow field</title>
<style>
html,body{margin:0;padding:0;background:#000;overflow:hidden}
#wrap{position:relative;width:900px;height:600px}
canvas{display:block}
#hud{position:absolute;top:8px;left:10px;color:#7dff9a;font:13px/1.5 monospace;
text-shadow:0 0 4px #000;pointer-events:none;white-space:pre}
</style>
</head>
<body>
<div id="wrap">
<canvas id="c" width="900" height="600"></canvas>
<div id="hud">FPS: --<br>particles: --</div>
</div>
<script>
(function(){
var W = 900, H = 600;
var N = 100000;
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
var hud = document.getElementById('hud');
// Interleaved SoA-in-one-array: [x0,y0,vx0,vy0, x1,y1,vx1,vy1, ...]
var P = new Float32Array(N * 4);
// seed
for (var i = 0; i < N; i++) {
P[i*4+0] = Math.random() * W;
P[i*4+1] = Math.random() * H;
P[i*4+2] = 0;
P[i*4+3] = 0;
}
// flow field params
var t = 0;
var S1 = 0.0016, S2 = 0.0031, S3 = 0.0009; // spatial scales
var T1 = 0.11, T2 = 0.07, T3 = 0.16; // temporal speeds
var A1 = 1.0, A2 = 0.6, A3 = 1.4; // amplitudes
// precomputed per-frame phase terms (cheap)
var ph = [0,0,0,0,0,0];
ctx.fillStyle = '#000';
ctx.fillRect(0,0,W,H);
// FPS honest measurement: EMA of frame delta
var last = performance.now();
var fpsEMA = 60;
var frames = 0;
var SIZE = 1.7;
function field(x, y, out) {
// three rotating sinusoidal "eddies" -> curl-like, time evolving
var a = x * S1, b = y * S1;
var c = x * S2, d = y * S2;
var e = x * S3, f = y * S3;
// term 1
var s1 = Math.sin(a + ph[0]) + Math.cos(b + ph[1]);
var c1 = Math.cos(a + ph[0]) - Math.sin(b + ph[1]);
// term 2
var s2 = Math.sin(c + ph[2]) + Math.cos(d + ph[3]);
var c2 = Math.cos(c + ph[2]) - Math.sin(d + ph[3]);
// term 3
var s3 = Math.sin(e + ph[4]) + Math.cos(f + ph[5]);
var c3 = Math.cos(e + ph[4]) - Math.sin(f + ph[5]);
// curl: perpendicular to gradient-ish combination
var fx = (c1 * A1) + (s2 * A2) + (c3 * A3);
var fy = (s1 * A1) + (c2 * A2) + (s3 * A3);
out[0] = fx;
out[1] = fy;
}
var F = [0,0];
function frame(now) {
var dt = now - last;
last = now;
if (dt > 0) {
var inst = 1000 / dt;
fpsEMA += (inst - fpsEMA) * 0.1;
}
frames++;
// evolve field phases
t += 0.016;
ph[0] = t * T1; ph[1] = t * T1 * 1.3 + 1.7;
ph[2] = t * T2 + 0.9; ph[3] = t * T2 * 0.8 + 4.1;
ph[4] = t * T3 + 2.2; ph[5] = t * T3 * 1.1 + 0.4;
// ---- UPDATE ----
var drag = 0.985;
for (var i = 0; i < N; i++) {
var k = i * 4;
var x = P[k], y = P[k+1];
field(x, y, F);
var vx = P[k+2] + F[0] * 0.35;
var vy = P[k+3] + F[1] * 0.35;
vx *= drag; vy *= drag;
x += vx; y += vy;
// recycle
if (x < -4) x = W + 4; else if (x > W + 4) x = -4;
if (y < -4) y = H + 4; else if (y > H + 4) y = -4;
P[k] = x; P[k+1] = y; P[k+2] = vx; P[k+3] = vy;
}
// ---- DRAW ----
// trail: translucent fade
ctx.fillStyle = 'rgba(0,0,0,0.045)';
ctx.fillRect(0,0,W,H);
ctx.fillStyle = '#9dffc4';
for (var j = 0; j < N; j++) {
var m = j * 4;
ctx.fillRect(P[m], P[m+1], SIZE, SIZE);
}
// HUD (throttle to every 6 frames)
if ((frames & 7) === 0) {
hud.innerHTML = 'FPS: ' + fpsEMA.toFixed(1) + '<br>particles: ' + N;
}
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
})();
</script>
</body>
</html>
FPS I expect: ~52 on a 2023 laptop, integrated graphics, Chrome. I'm committing to 52, accepting a ±5 band. The honest risk is it lands in the mid-40s; I'm not claiming 60 because 100k individual fillRect calls is genuinely at the edge of what Canvas 2D rasterization sustains, and the translucent full-screen fade rect adds a full-canvas composite every frame.
Where the time goes: roughly 30% update / 70% draw. The update loop is a tight Float32Array scan doing ~6 sin/cos per particle — that's ~600k transcendental calls per frame, which is real but modern JITs chew through it in a few ms. The draw side is 100k separate fillRect calls, each a driver call into the rasterizer with state validation and a small-rect blit; that's the dominant cost, plus the full-screen alpha fade composite. The fade rect is a deliberate trade for the ink-in-water look and it costs maybe 1–2 ms.
The first thing that breaks at a million: the per-particle fillRect call count. You go from 100k driver calls to 1M driver calls and the rasterizer falls off a cliff — you'd drop to single-digit FPS. The update loop would still be fine; it's purely the draw-call throughput.
To go ten times faster: batched path rendering — build one Path2D (or a single beginPath with 100k rect() subpaths) and issue one fill() per frame instead of 100k fillRect calls. That collapses the draw from 100k driver calls to 1, moving the bottleneck back to the JS update loop and the single large rasterization. That's the named technique: single-path batching.
Nothing dropped. 100,000 particles, real per-particle position/velocity updated every frame, a genuinely time-evolving curl-like flow field (three rotating sinusoidal eddy terms with independent spatial and temporal phases, so the field swirls and drifts rather than settling), recycling on exit, honest EMA-measured FPS, and the particle count read from the actual Float32Array length (N, the real data structure). One file, no libraries, no network, no storage.