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.
All 100,000 particles live in one interleaved Float32Array (x, y, vx, vy) that seeds two ping-pong GPU buffers; after upload, JavaScript never touches particle data again. Each frame, a hand-rolled WebGL2 transform-feedback vertex shader advects every particle through an analytic curl field — the curl of a potential built from four plane-wave octaves whose wave directions slowly rotate and whose phases scroll, plus a turning large-scale wind and a pointer-driven vortex — and writes the new state into the other buffer, so each particle is individually updated on the GPU. Drawing is one additive pass of 1-px points into the default framebuffer; trails come from a full-screen reverse-subtract blend that fades the previous frame toward true black (the tiny subtract term cures the classic 8-bit "ghost trails never reach zero" bug). The FPS/count HUD is DOM text refreshed twice a second from a real windowed average of rAF deltas. The one thing I expect to be the bottleneck: not the particles — total GPU work is ~1 ms, and the single most expensive item inside that is the full-screen fade's 540,000-pixel blend read-modify-write; the frame is vsync-paced, so the display, not the sim, sets the number.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>CURL/100K — flow-field particles</title>
<style>
:root{
--bg:#07090d; --line:#1b2531; --ink:#e9eef4; --dim:#5e6f82; --faint:#415062;
--amber:#ffb454; --teal:#3fd6de;
--mono:ui-monospace,"SF Mono","Cascadia Mono",Menlo,Consolas,"Liberation Mono",monospace;
--sans:"Avenir Next","Segoe UI Variable Text","Segoe UI",system-ui,-apple-system,sans-serif;
}
*{box-sizing:border-box}
html,body{margin:0}
body{
min-height:100vh; display:grid; place-items:center; padding:28px 16px;
background:
radial-gradient(1100px 700px at 18% -10%, rgba(63,214,222,.07), transparent 60%),
radial-gradient(900px 620px at 105% 112%, rgba(255,180,84,.06), transparent 60%),
var(--bg);
color:var(--ink); font-family:var(--sans);
}
.wrap{width:900px; max-width:100%}
header{display:flex; justify-content:space-between; align-items:baseline; gap:12px; padding:0 2px 10px}
h1{margin:0; font-size:15px; font-weight:800; letter-spacing:.16em; text-transform:uppercase}
h1 em{font-style:normal; color:var(--teal)}
.meta{font:11px/1.4 var(--mono); color:var(--dim); letter-spacing:.04em; text-align:right}
.dot{display:inline-block; width:6px; height:6px; border-radius:50%; background:var(--teal);
margin-right:6px; vertical-align:1px; animation:pulse 2.2s ease-in-out infinite}
@keyframes pulse{50%{opacity:.3}}
#stage{position:relative; border:1px solid var(--line); background:#000;
box-shadow:0 24px 80px rgba(0,0,0,.55); transition:border-color .4s, box-shadow .4s}
#stage:hover{border-color:#25364c; box-shadow:0 24px 80px rgba(0,0,0,.55), 0 0 44px rgba(63,214,222,.07)}
#stage::before,#stage::after{content:""; position:absolute; width:14px; height:14px;
border:1px solid rgba(63,214,222,.45); pointer-events:none}
#stage::before{top:-5px; left:-5px; border-right:none; border-bottom:none}
#stage::after{bottom:-5px; right:-5px; border-left:none; border-top:none}
canvas{display:block; width:100%; height:auto; cursor:crosshair; touch-action:none}
.panel{position:absolute; background:rgba(5,8,12,.8); border:1px solid rgba(120,150,180,.16);
padding:8px 12px; pointer-events:none; user-select:none; transition:border-color .3s}
#stage:hover .panel{border-color:rgba(130,170,200,.32)}
.label{font:600 9px/1 var(--mono); letter-spacing:.24em; text-transform:uppercase; color:var(--dim); margin-bottom:6px}
#fpsPanel{top:12px; left:12px; min-width:118px}
#fpsPanel .num{font:700 30px/1 var(--mono); color:var(--amber); font-variant-numeric:tabular-nums}
#fpsPanel .unit{font:600 11px var(--mono); color:var(--dim); margin-left:5px}
#fpsPanel .sub{font:10.5px/1.5 var(--mono); color:var(--dim); margin-top:5px}
#countPanel{top:12px; right:12px; text-align:right}
#countPanel .num{font:700 19px/1 var(--mono); color:var(--ink); font-variant-numeric:tabular-nums}
#countPanel .sub{font:10px/1.6 var(--mono); color:var(--faint); margin-top:5px}
#hint{bottom:12px; left:12px; font:10.5px/1.4 var(--mono); color:var(--faint);
background:none; border-color:transparent; transition:color .3s}
#stage:hover #hint{color:var(--dim)}
#legend{bottom:12px; right:12px; display:flex; align-items:center; gap:8px;
font:600 9px var(--mono); letter-spacing:.18em; text-transform:uppercase; color:var(--dim)}
#legend .bar{width:92px; height:4px; background:linear-gradient(90deg,#0c3f4c,var(--teal) 42%,var(--amber))}
footer{display:flex; justify-content:space-between; gap:12px; padding:10px 2px 0;
font:10.5px/1.5 var(--mono); color:var(--faint)}
.fail{padding:60px 30px; font:13px/1.7 var(--mono); color:#ff8f6b; white-space:pre-wrap}
</style>
</head>
<body>
<div class="wrap">
<header>
<h1>CURL<em>/</em>100K</h1>
<div class="meta"><span class="dot"></span>live · webgl2 transform feedback · zero libraries</div>
</header>
<div id="stage">
<canvas id="c" width="900" height="600"></canvas>
<div class="panel" id="fpsPanel">
<div class="label">frame rate</div>
<div><span class="num" id="fps">--</span><span class="unit">fps</span></div>
<div class="sub"><span id="ms">--</span> ms/frame</div>
</div>
<div class="panel" id="countPanel">
<div class="label">particles live</div>
<div class="num" id="count">--</div>
<div class="sub">state: 2 × 1.6 MB ping-pong<br>field clock <span id="ft">0.0</span> s</div>
</div>
<div class="panel" id="hint">pointer stirs a vortex · press for a harder kick</div>
<div class="panel" id="legend">slow <span class="bar"></span> fast</div>
</div>
<footer>
<span>analytic curl of 4 rotating plane-wave octaves + drifting wind</span>
<span>900×600 · additive 1px points · trail fade 0.934</span>
</footer>
</div>
<script>
'use strict';
(function(){
const W = 900, H = 600, N = 100000;
const stage = document.getElementById('stage');
const canvas = document.getElementById('c');
const fpsEl = document.getElementById('fps'), msEl = document.getElementById('ms'),
countEl = document.getElementById('count'), ftEl = document.getElementById('ft');
function fail(msg){
stage.innerHTML = '<div class="fail">' + msg + '</div>';
throw new Error(msg);
}
let gl = null;
try {
gl = canvas.getContext('webgl2', {
alpha:false, depth:false, stencil:false, antialias:false,
premultipliedAlpha:true, preserveDrawingBuffer:false,
powerPreference:'high-performance'
});
} catch(e){}
if(!gl) fail('WebGL2 is unavailable in this browser/context.\nThis build needs hardware-accelerated WebGL2 (transform feedback).');
/* ---------------- particle store ----------------
One interleaved Float32Array: x, y, vx, vy per particle.
It seeds two GPU buffers; after this the CPU never touches it. */
const state = new Float32Array(N * 4);
for(let i = 0; i < N; i++){
state[i*4] = Math.random() * W;
state[i*4+1] = Math.random() * H;
}
const COUNT = state.length >> 2; // count read from the actual store
countEl.textContent = COUNT.toLocaleString('en-US');
/* ---------------- shaders ---------------- */
const VS_UPDATE = `#version 300 es
precision highp float;
layout(location = 0) in vec4 aPosVel;
out vec4 vPosVel;
uniform vec2 uSize;
uniform float uDt;
uniform float uLerp;
uniform float uSeed;
uniform vec2 uDir[4];
uniform float uFreq[4];
uniform float uPhase[4];
uniform float uAmp[4];
uniform vec2 uWind;
uniform vec2 uMouse;
uniform float uSwirl;
float hash(float n){ return fract(sin(n) * 43758.5453123); }
vec2 fieldVel(vec2 p){
vec2 q = p * 0.0075;
vec2 v = uWind;
for(int i = 0; i < 4; i++){
vec2 d = uDir[i];
float w = dot(q, d) * uFreq[i] + uPhase[i];
v += uAmp[i] * vec2(d.y, -d.x) * cos(w);
}
vec2 m = p - uMouse;
float r = length(m) + 1e-4;
v += uSwirl * exp(-r * r * 0.00011) * vec2(-m.y, m.x) / r;
return v;
}
void main(){
vec2 p = aPosVel.xy;
vec2 vel = aPosVel.zw;
vec2 fv = fieldVel(p);
vel += (fv - vel) * uLerp;
p += vel * uDt;
if(p.x < -3.0 || p.x > uSize.x + 3.0 || p.y < -3.0 || p.y > uSize.y + 3.0){
float id = float(gl_VertexID);
p = vec2(hash(id * 0.0671 + uSeed * 1.31),
hash(id * 0.1377 - uSeed * 0.77 + 3.7)) * uSize;
vel = fieldVel(p);
}
vPosVel = vec4(p, vel);
}`;
const FS_NULL = `#version 300 es
precision mediump float;
out vec4 outColor;
void main(){ outColor = vec4(0.0); }`;
const VS_FADE = `#version 300 es
void main(){
vec2 v = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));
gl_Position = vec4(v * 2.0 - 1.0, 0.0, 1.0);
}`;
const FS_FADE = `#version 300 es
precision mediump float;
uniform float uFade;
out vec4 outColor;
void main(){ outColor = vec4(vec3(uFade), 1.0); }`;
const VS_POINT = `#version 300 es
precision highp float;
layout(location = 0) in vec4 aPosVel;
uniform vec2 uSize;
out vec3 vColor;
float hash(float n){ return fract(sin(n) * 43758.5453123); }
void main(){
vec2 ndc = aPosVel.xy / uSize * 2.0 - 1.0;
gl_Position = vec4(ndc.x, -ndc.y, 0.0, 1.0);
gl_PointSize = 1.0;
float sp = length(aPosVel.zw);
float s = clamp(sp * 0.015, 0.0, 1.0);
float g = 0.8 + 0.4 * hash(float(gl_VertexID) * 0.618);
vec3 cold = vec3(0.05, 0.28, 0.36);
vec3 warm = vec3(1.00, 0.60, 0.24);
vColor = mix(cold, warm, s * s) * (0.05 + 0.09 * s) * g;
}`;
const FS_POINT = `#version 300 es
precision mediump float;
in vec3 vColor;
out vec4 outColor;
void main(){ outColor = vec4(vColor, 1.0); }`;
function compile(type, src){
const s = gl.createShader(type);
gl.shaderSource(s, src); gl.compileShader(s);
if(!gl.getShaderParameter(s, gl.COMPILE_STATUS)) fail('Shader compile failed:\n' + gl.getShaderInfoLog(s));
return s;
}
function program(vsSrc, fsSrc, tfVaryings){
const p = gl.createProgram();
gl.attachShader(p, compile(gl.VERTEX_SHADER, vsSrc));
gl.attachShader(p, compile(gl.FRAGMENT_SHADER, fsSrc));
if(tfVaryings) gl.transformFeedbackVaryings(p, tfVaryings, gl.INTERLEAVED_ATTRIBS);
gl.linkProgram(p);
if(!gl.getProgramParameter(p, gl.LINK_STATUS)) fail('Program link failed:\n' + gl.getProgramInfoLog(p));
return p;
}
const pUp = program(VS_UPDATE, FS_NULL, ['vPosVel']);
const pFade = program(VS_FADE, FS_FADE);
const pPts = program(VS_POINT, FS_POINT);
const loc = {
dt: gl.getUniformLocation(pUp, 'uDt'), lerp: gl.getUniformLocation(pUp, 'uLerp'),
seed: gl.getUniformLocation(pUp, 'uSeed'), sizeU: gl.getUniformLocation(pUp, 'uSize'),
dir: gl.getUniformLocation(pUp, 'uDir'), freq: gl.getUniformLocation(pUp, 'uFreq'),
phase: gl.getUniformLocation(pUp, 'uPhase'), amp: gl.getUniformLocation(pUp, 'uAmp'),
wind: gl.getUniformLocation(pUp, 'uWind'), mouse: gl.getUniformLocation(pUp, 'uMouse'),
swirl: gl.getUniformLocation(pUp, 'uSwirl'),
fade: gl.getUniformLocation(pFade, 'uFade'),
sizeP: gl.getUniformLocation(pPts, 'uSize')
};
/* ---------------- GPU state: two ping-pong buffers ---------------- */
const bufs = [gl.createBuffer(), gl.createBuffer()];
const vaos = [gl.createVertexArray(), gl.createVertexArray()];
for(let i = 0; i < 2; i++){
gl.bindBuffer(gl.ARRAY_BUFFER, bufs[i]);
gl.bufferData(gl.ARRAY_BUFFER, state, gl.DYNAMIC_COPY);
gl.bindVertexArray(vaos[i]);
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(0, 4, gl.FLOAT, false, 16, 0);
}
gl.bindVertexArray(null);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
const tf = gl.createTransformFeedback();
const vaoEmpty = gl.createVertexArray(); // fullscreen triangle via gl_VertexID
gl.useProgram(pPts); gl.uniform2f(loc.sizeP, W, H);
gl.useProgram(pUp); gl.uniform2f(loc.sizeU, W, H);
gl.useProgram(pFade); gl.uniform1f(loc.fade, 0.934);
const FADE_DITHER = 1.6 / 255; // kills 8-bit fade stiction
gl.blendColor(FADE_DITHER, FADE_DITHER, FADE_DITHER, 1);
gl.viewport(0, 0, W, H);
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
/* ---------------- the evolving field (CPU feeds uniforms only) ---------------- */
const octaves = [
{f:1.6, a:30, th:0.8, wr: 0.043, ph:0.0, wp: 0.16},
{f:3.1, a:24, th:2.4, wr:-0.067, ph:1.7, wp:-0.23},
{f:5.6, a:17, th:4.1, wr: 0.094, ph:3.3, wp: 0.31},
{f:9.3, a:11, th:5.6, wr:-0.130, ph:5.0, wp:-0.47}
];
const dirs = new Float32Array(8), freqs = new Float32Array(4),
phases = new Float32Array(4), amps = new Float32Array(4);
octaves.forEach((o, i) => { freqs[i] = o.f; amps[i] = o.a; });
let mx = W * 0.5, my = H * 0.5, swirl = 0;
function pointerPos(e){
const r = canvas.getBoundingClientRect();
mx = (e.clientX - r.left) * W / r.width;
my = (e.clientY - r.top) * H / r.height;
}
canvas.addEventListener('pointermove', e => { pointerPos(e); swirl = Math.max(swirl, 300); });
canvas.addEventListener('pointerdown', e => { pointerPos(e); swirl = 560; });
/* ---------------- frame loop ---------------- */
let cur = 0, simT = 0, last = -1;
let accMs = 0, accFrames = 0;
function frame(now){
requestAnimationFrame(frame);
if(last < 0){ last = now; return; }
let raw = now - last; last = now;
if(raw > 250) raw = 16.7; // tab was hidden; don't poison the average
const dt = Math.min(raw, 50) / 1000;
simT += dt;
// honest FPS: windowed average of real frame deltas
accMs += raw; accFrames++;
if(accMs >= 500){
fpsEl.textContent = Math.round(accFrames * 1000 / accMs);
msEl.textContent = (accMs / accFrames).toFixed(2);
ftEl.textContent = simT.toFixed(1);
accMs = 0; accFrames = 0;
}
// evolve the field: rotate wave directions, scroll phases, turn the wind
for(let i = 0; i < 4; i++){
const o = octaves[i], ang = o.th + o.wr * simT;
dirs[i*2] = Math.cos(ang); dirs[i*2+1] = Math.sin(ang);
phases[i] = o.ph + o.wp * simT;
}
const wa = 0.9 + simT * 0.013 + 0.6 * Math.sin(simT * 0.037);
swirl *= Math.exp(-dt * 2.2);
// pass 1: transform feedback — every particle updated individually on the GPU
const src = cur, dst = 1 - cur;
gl.useProgram(pUp);
gl.uniform1f(loc.dt, dt);
gl.uniform1f(loc.lerp, 1 - Math.exp(-4.2 * dt));
gl.uniform1f(loc.seed, simT % 1000);
gl.uniform2fv(loc.dir, dirs);
gl.uniform1fv(loc.freq, freqs);
gl.uniform1fv(loc.phase, phases);
gl.uniform1fv(loc.amp, amps);
gl.uniform2f(loc.wind, 8.5 * Math.cos(wa), 8.5 * Math.sin(wa));
gl.uniform2f(loc.mouse, mx, my);
gl.uniform1f(loc.swirl, swirl);
gl.bindVertexArray(vaos[src]);
gl.enable(gl.RASTERIZER_DISCARD);
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, bufs[dst]);
gl.beginTransformFeedback(gl.POINTS);
gl.drawArrays(gl.POINTS, 0, COUNT);
gl.endTransformFeedback();
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null);
gl.disable(gl.RASTERIZER_DISCARD);
// pass 2: fade previous frame toward true black (dst*f - epsilon)
gl.enable(gl.BLEND);
gl.blendEquation(gl.FUNC_REVERSE_SUBTRACT);
gl.blendFuncSeparate(gl.CONSTANT_COLOR, gl.SRC_COLOR, gl.ZERO, gl.ONE);
gl.useProgram(pFade);
gl.bindVertexArray(vaoEmpty);
gl.drawArrays(gl.TRIANGLES, 0, 3);
// pass 3: additive 1-px points from the freshly written buffer
gl.blendEquation(gl.FUNC_ADD);
gl.blendFunc(gl.ONE, gl.ONE);
gl.useProgram(pPts);
gl.bindVertexArray(vaos[dst]);
gl.drawArrays(gl.POINTS, 0, COUNT);
gl.disable(gl.BLEND);
cur = dst;
}
requestAnimationFrame(frame);
canvas.addEventListener('webglcontextlost', e => {
e.preventDefault();
fail('WebGL context lost — reload the page.');
});
})();
</script>
</body>
</html>
Nothing dropped.