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.
Particles live in one interleaved Float32Array of 100,000 records (x, y, vx, vy). A 72×48 vector grid is rebuilt every frame from time-varying sinusoids (curl-like: orthogonal combinations so the field shears and eddies instead of pointing at sinks), then each particle bilinear-samples that grid, damps toward it, and Euler-steps; leavers are respawned on a random edge so the array length never changes. Drawing is raw WebGL1 — a fullscreen fade triangle for ink trails, then a single POINTS draw of the whole buffer. The bottleneck I expect is the JavaScript integration loop, not fill rate.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>flow</title>
<style>
html, body {
margin: 0; height: 100%; background: #000;
display: flex; align-items: center; justify-content: center;
}
#wrap { position: relative; width: 900px; height: 600px; }
canvas { display: block; width: 900px; height: 600px; background: #02040a; }
#hud {
position: absolute; left: 10px; top: 8px;
color: #eaf1ff;
font: 14px/1.35 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
text-shadow: 0 1px 2px #000;
pointer-events: none; user-select: none; letter-spacing: 0.02em;
}
</style>
</head>
<body>
<div id="wrap">
<canvas id="c" width="900" height="600"></canvas>
<div id="hud"><span id="fps">—</span> fps · <span id="n">0</span> particles</div>
</div>
<script>
(function () {
var W = 900, H = 600, N = 100000;
var GW = 72, GH = 48;
var canvas = document.getElementById('c');
var gl = canvas.getContext('webgl', {
preserveDrawingBuffer: true,
alpha: false,
antialias: false,
depth: false,
stencil: false,
premultipliedAlpha: false
});
if (!gl) {
document.getElementById('fps').textContent = 'no webgl';
return;
}
function sh(type, src) {
var s = gl.createShader(type);
gl.shaderSource(s, src);
gl.compileShader(s);
if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) throw new Error(gl.getShaderInfoLog(s));
return s;
}
function prog(vs, fs) {
var p = gl.createProgram();
gl.attachShader(p, sh(gl.VERTEX_SHADER, vs));
gl.attachShader(p, sh(gl.FRAGMENT_SHADER, fs));
gl.linkProgram(p);
if (!gl.getProgramParameter(p, gl.LINK_STATUS)) throw new Error(gl.getProgramInfoLog(p));
return p;
}
var fadeP = prog(
'attribute vec2 a_p; void main(){ gl_Position = vec4(a_p, 0.0, 1.0); }',
'precision mediump float; uniform vec4 u_c; void main(){ gl_FragColor = u_c; }'
);
var partP = prog(
'attribute vec2 a_pos; attribute vec2 a_vel; uniform vec2 u_res; varying float v_s;' +
'void main(){' +
' vec2 c = a_pos / u_res * 2.0 - 1.0;' +
' gl_Position = vec4(c.x, -c.y, 0.0, 1.0);' +
' gl_PointSize = 2.0;' +
' v_s = clamp(length(a_vel) * 0.22, 0.0, 1.0);' +
'}',
'precision mediump float; varying float v_s;' +
'void main(){' +
' vec2 p = gl_PointCoord - 0.5;' +
' float d = dot(p, p);' +
' if (d > 0.25) discard;' +
' float a = smoothstep(0.25, 0.0, d);' +
' vec3 col = mix(vec3(0.16, 0.48, 0.95), vec3(0.88, 0.94, 1.0), v_s);' +
' gl_FragColor = vec4(col, a * 0.5);' +
'}'
);
var fadeAp = gl.getAttribLocation(fadeP, 'a_p');
var fadeUc = gl.getUniformLocation(fadeP, 'u_c');
var partApos = gl.getAttribLocation(partP, 'a_pos');
var partAvel = gl.getAttribLocation(partP, 'a_vel');
var partUres = gl.getUniformLocation(partP, 'u_res');
var quadBuf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, quadBuf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 3, -1, -1, 3]), gl.STATIC_DRAW);
var data = new Float32Array(N * 4);
var i, o;
for (i = 0; i < N; i++) {
o = i * 4;
data[o] = Math.random() * W;
data[o + 1] = Math.random() * H;
data[o + 2] = (Math.random() - 0.5) * 0.5;
data[o + 3] = (Math.random() - 0.5) * 0.5;
}
var pbuf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, pbuf);
gl.bufferData(gl.ARRAY_BUFFER, data.byteLength, gl.DYNAMIC_DRAW);
var field = new Float32Array(GW * GH * 2);
var invW = GW / W, invH = GH / H;
var GWm1 = GW - 2, GHm1 = GH - 2;
function updateField(ms) {
var t = ms * 0.00021;
var t2 = ms * 0.00011;
var j, i, nx, ny, e1, e2, e3, k;
for (j = 0; j < GH; j++) {
ny = j * 0.1308997;
for (i = 0; i < GW; i++) {
nx = i * 0.08726646;
e1 = Math.cos(nx * 1.0 + ny * 1.4 + t);
e2 = Math.cos(nx * 2.2 - ny * 1.8 - t2 * 1.6);
e3 = Math.cos(-nx * 0.7 + ny * 3.0 + t * 0.65);
k = (j * GW + i) << 1;
field[k] = 1.15 * e1 + 0.75 * e2 + 0.45 * e3 + 0.28 * Math.sin(ny + t * 0.4);
field[k + 1] = -0.95 * e1 + 0.65 * e2 - 0.55 * e3 + 0.18 * Math.cos(nx - t2);
}
}
}
function integrate() {
var f = field, d = data, n = N;
var x, y, vx, vy, gx, gy, ix, iy, tx, ty, row, i00, i10, i01, i11;
var ax, ay, bx, by, cx, cy, dx, dy, ux, uy, px, py, fx, fy, side, r;
for (i = 0, o = 0; i < n; i++, o += 4) {
x = d[o]; y = d[o + 1]; vx = d[o + 2]; vy = d[o + 3];
gx = x * invW;
gy = y * invH;
ix = gx | 0;
iy = gy | 0;
if (ix < 0) ix = 0; else if (ix > GWm1) ix = GWm1;
if (iy < 0) iy = 0; else if (iy > GHm1) iy = GHm1;
tx = gx - ix;
ty = gy - iy;
row = iy * GW;
i00 = (row + ix) << 1;
i10 = (row + ix + 1) << 1;
i01 = (row + GW + ix) << 1;
i11 = (row + GW + ix + 1) << 1;
ax = f[i00]; ay = f[i00 + 1];
bx = f[i10]; by = f[i10 + 1];
cx = f[i01]; cy = f[i01 + 1];
dx = f[i11]; dy = f[i11 + 1];
ux = ax + (bx - ax) * tx;
uy = ay + (by - ay) * tx;
px = cx + (dx - cx) * tx;
py = cy + (dy - cy) * tx;
fx = ux + (px - ux) * ty;
fy = uy + (py - uy) * ty;
vx = vx * 0.965 + fx * 0.055;
vy = vy * 0.965 + fy * 0.055;
x += vx;
y += vy;
if (x < 0 || x >= W || y < 0 || y >= H) {
side = (Math.random() * 4) | 0;
r = Math.random();
if (side === 0) { x = r * W; y = 0.5; }
else if (side === 1) { x = r * W; y = H - 0.5; }
else if (side === 2) { x = 0.5; y = r * H; }
else { x = W - 0.5; y = r * H; }
vx *= 0.15;
vy *= 0.15;
}
d[o] = x; d[o + 1] = y; d[o + 2] = vx; d[o + 3] = vy;
}
}
gl.enable(gl.BLEND);
gl.clearColor(0.012, 0.02, 0.045, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(partP);
gl.uniform2f(partUres, W, H);
var fpsEl = document.getElementById('fps');
var nEl = document.getElementById('n');
nEl.textContent = String(data.length >> 2);
var prev = performance.now();
var accT = 0, accF = 0;
function frame(now) {
var dt = now - prev;
prev = now;
accT += dt;
accF += 1;
if (accT >= 500) {
fpsEl.textContent = (1000 * accF / accT).toFixed(1);
nEl.textContent = String(data.length >> 2);
accT = 0;
accF = 0;
}
updateField(now);
integrate();
gl.bindBuffer(gl.ARRAY_BUFFER, pbuf);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, data);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.useProgram(fadeP);
gl.bindBuffer(gl.ARRAY_BUFFER, quadBuf);
gl.enableVertexAttribArray(fadeAp);
gl.vertexAttribPointer(fadeAp, 2, gl.FLOAT, false, 0, 0);
if (partAvel >= 0) gl.disableVertexAttribArray(partAvel);
gl.uniform4f(fadeUc, 0.012, 0.022, 0.05, 0.11);
gl.drawArrays(gl.TRIANGLES, 0, 3);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
gl.useProgram(partP);
gl.bindBuffer(gl.ARRAY_BUFFER, pbuf);
gl.enableVertexAttribArray(partApos);
gl.enableVertexAttribArray(partAvel);
gl.vertexAttribPointer(partApos, 2, gl.FLOAT, false, 16, 0);
gl.vertexAttribPointer(partAvel, 2, gl.FLOAT, false, 16, 8);
gl.drawArrays(gl.POINTS, 0, data.length >> 2);
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
})();
</script>
</body>
</html>
bufferSubData of 1.6 MB and one POINTS pass are cheap on an iGPU at this resolution. Frame time should sit around 6–9 ms, so vsync holds 60.integrate() — ten times the particles is ~50 ms of JS before the GPU does anything, so you drop to the high teens. Upload size (16 MB/frame) is second.Nothing dropped.