# No oracle for a meadow
> Everything else I've handed to an agent could be checked against a number. This one couldn't: one sentence to Claude Opus 5, 1,922 lines of WebGL, and a honeybee in backlight. What stands in for a test when the only judge is your eye.
Source: https://rfriedmann.de/blog/no-oracle-for-a-meadow/
Published: 2026-08-10 · Track: log · Level: Advanced
Nearly every post in this log has ended on a number. [imp's decode
speed](/blog/gguf-decode-beats-llama-cpp/) against llama.cpp. [A CUTLASS bump gated
by an fp64 oracle](/blog/bumping-a-dependency-you-cant-read/). [97,000 lines of CUDA
that I can't read, made safe by tests I can](/blog/writing-cuda-with-an-agent/). The
argument in all of them is the same: you don't have to review what a machine wrote if
you can *verify* it.
So here is the case that breaks the argument. I typed one sentence at
[Claude Opus 5](https://claude.ai/claude-code) in Claude Code:
> lass ein bienchen im browser durch eine schöne grüne wiese fliegen.. beste
> grafiken, letzte standards, 3D
("let a little bee fly through a nice green meadow in the browser.. best graphics,
latest standards, 3D.") Everything in [bienchen](https://github.com/kekzl/bienchen)
came out of that: geometry, shaders, flight behaviour, camera work, deployment. My
only other contribution was saying "start it up" once. It runs at
[bienchen.kekz.org](https://bienchen.kekz.org).
The "Weite" setup, one of five, captured headless for this post. This is the whole argument of the scene in one frame: the camera is looking into the sun on purpose, and everything thin — grass, stems, petals — lights up from behind rather than falling into shadow.
There is no oracle for this. Not "hard to write one" — there cannot be one. The
success criterion is *does it look like a summer meadow in low sun*, and no fp64
reference on earth returns that. Which makes it the more interesting experiment: not
what an agent builds when it can check itself, but what it reaches for when nothing
can tell it whether it's right.
1
sentence, the entire specification
1,922
lines of JavaScript across twelve modules, in one commit
233,000
grass blades on the top quality setting
0
tests, build steps, and runtime network requests
## What it reached for first
Given "best graphics" and no further constraints, there's an obvious way to spend the
budget: more features. Shadow maps, screen-space reflections, volumetric light shafts,
a physically-based material on everything. Feature count is what a graphics demo
usually optimises, because feature count is the thing you can list.
That is not what happened. The first decision in the codebase is a *lighting*
decision, and every other decision bends around it. `config.js`:
```js
export const SUN = {
elevation: 4.2, // degrees above the horizon
azimuth: 168,
};
```
Four point two degrees. The sun is essentially on the horizon, which means almost
nothing in the scene is lit from the front — it's lit from *behind*, through leaves
and petals. The README states the thesis in one line: "Gegenlicht ist das ganze
Konzept." Backlight is the whole concept.
Every surface in the meadow carries a term for it. In the grass fragment shader, the
whole effect is two lines:
```glsl
// backlight through the leaf
float back = pow(clamp(dot(V, -L), 0.0, 1.0), 3.0);
col += uTransCol * uSunColor * back * pow(vSway, 1.5) * sh * uTrans;
```
`dot(V, -L)` is "how directly is the camera looking into the sun", and `vSway` is how
far up the blade you are — so a blade glows more at the tip than at the base, and more
when you're staring into the light. The flowers use the same term with a per-species
strength baked into a vertex attribute, and the poppy's is the highest in the scene at
`1.7`, against `1.3` for the cornflower and `0.35` for stems. The poppy is also the
only saturated colour in the entire palette. One accent, and the light picked to make
that accent burn.
The "Halmspitzen" setup, parked just above the tallest blade. Four species are in this frame and only one of them is allowed to be loud — the daisies are near-white, the cornflowers sit dark against the grass, and the poppies carry both the strongest colour and the strongest transmission constant.
## The camera knows where the sun is
Here's the part I didn't expect. A backlit *scene* is a shader decision. Making sure
the camera actually stands in the backlight is a **cinematography** decision, and it's
in `director.js` as executable code:
```js
// The angle from which the camera looks into the sun, seen from the subject
const INTO_SUN = Math.atan2(-sun.z, -sun.x);
/** Random azimuth, scattered `spread` radians around the backlit direction. */
function backlitAngle(spread) {
return INTO_SUN + (Math.random() - 0.5) * 2 * spread;
}
```
Four of the five camera setups place themselves with `backlitAngle()`. The fifth, the
tracking shot, sits behind the bee along its flight path, so it inherits whatever the
bee is doing. And the flank shot doesn't randomise its side at all — it *computes*
which side to sit on so that the sun ends up opposite:
```js
// The side isn't rolled, it's chosen so the sun stands opposite.
cut(s, c) { s.side = (c.right.x * -sun.x + c.right.z * -sun.z) >= 0 ? 1 : -1; },
```
That's a dot product doing the job of a director of photography. Nobody asked for it.
"Beste Grafiken" doesn't imply it. It's what you do if your actual goal is a shot that
looks good rather than a renderer that ticks boxes.
The same instinct shows up in what the camera *refuses* to do. It cuts hard between
setups — no smooth interpolation, no crane moves — and the comment says why: soft
moves "would make the camera the subject instead of the bee." A wildlife-documentary
grammar, chosen and then defended.
## Scale from nature, not from feel
The cheapest realism in any 3D scene is correct proportion, and it's the thing hobby
scenes almost always get wrong, because eyeballing sizes is faster than looking them
up. One world unit here is one centimetre, and the sizes are real ones:
What the scene actually does
Bee body ~1.4 cm — a real Apis mellifera worker is 12–15 mm
Near grass 34 cm base height, ×0.72–1.28 random, ×0.86–1.14 clumping
Poppy ~9 cm across the bloom, cornflower stem 38 cm, dandelion 23 cm
Bee held to 22–64 cm up, just above a 25–50 cm grass canopy
What usually gets built instead
Sizes tuned until the frame "looks balanced"
Uniform grass height, so the field reads as mown carpet
Flowers scaled up because they're hard to see otherwise
Flight speed picked to fill the shot, not to match an animal
The README is blunt about which of these matters: the proportions "come from nature,
not from feeling — that contributes more to the impression than any shader." I think
that's correct, and I think it's the single most useful sentence in the repository.
The clumping is the detail I'd have skipped. Grass placement is random, but the height
gets multiplied by a smooth noise field, with the comment "otherwise it looks mown."
Truly uniform randomness reads as artificial, because real meadows grow in patches.
That is a failure mode you only know about if you've looked at a field.
## The numbers taken from the display instead
Two measurements in the scene are wrong on purpose, and they're the most interesting
decisions in the repo. The sharper one is in `bee.js`:
```js
const FLAP_HZ = 17.5;
```
A honeybee beats its wings at roughly 230 Hz. This is off by more than a factor of
ten — and it has to be. A 60 Hz display can only represent motion up to 30 Hz before
it aliases; at 230 Hz the wings wouldn't blur, they'd strobe, freeze, and appear to run
backwards, the same wagon-wheel effect you get filming a spinning propeller.
A wingbeat you cannot render
[diagram omitted — see the page for the chart]
Nearly every dimension in the scene is taken from the animal. This one is taken from the display, because physical accuracy here would look strictly worse.
And then it goes one step further, which is the bit I actually admire. Slowing the
wings down fixes the aliasing but loses the smear — a real bee's wings read as a
translucent arc, not as two solid paddles. So each wing is drawn three times, with the
copies lagging behind in the stroke cycle and fading out:
```js
// Stragglers in the stroke cycle: they draw the arc instead of strobing
const GHOSTS = [
{ lag: 0.0, alpha: 1.0 },
{ lag: 0.70, alpha: 0.36 },
{ lag: 1.35, alpha: 0.19 },
];
```
The two details the code says carry the whole impression: the additive fuzz rim that only exists because the light is behind the animal, and wings that read as a fan rather than as paddles. Captured at the low quality preset, so no depth of field and no antialiasing here.
That's motion blur, hand-built out of three transparent copies, applied only to the
one object in the scene that needs it. The cost is two extra wing quads per wing, on a
four-winged insect. A general post-process motion blur would have cost a full-screen
pass and smeared the entire frame to fix one moving part.
The second deviation is quieter and the repo never remarks on it. Cruise speed in
`flight.js` is 105 cm/s; a real forager does something closer to 6.5 m/s. The scene's
flight radius is 140 cm, so at the honest speed the bee would cross the entire meadow
in under half a second and every shot would be a blur leaving frame. Same trade as the
wings, made silently.
So the rule isn't "copy nature." It's copy nature *everywhere it survives contact with
a 60 Hz rectangle*, and deviate only where it doesn't — which is a much harder rule to
follow, because it requires knowing which is which.
One honest wart while I'm in here: `FLAP_HZ` is defined in `bee.js` and then written
again as a bare `17.5` in `flight.js:141`, for the body bob. Two copies of one fact.
The [dependency-bump post](/blog/bumping-a-dependency-you-cant-read/) on this site is
entirely about not doing that. Nobody caught it, because nothing here *can* catch it.
## The comments are the artifact
Read enough of this repo and a pattern shows up. The comments are not describing what
the code does. They're describing what it looked like when it was wrong:
- On the grass arc: the instance matrix only scales Y, so the blade's own curvature has
to be multiplied by the blade height by hand — "without that the blades stand like
needles."
- On the bee's fuzz: without noise it'd be "a clean ring of light instead of hair," so
the additive rim gets two layers of fbm multiplied together to break it into tufts.
- On the low camera: it can't go any lower, because "at this density there's always a
blade directly in front of the lens."
- On the sky: HDR values around a 4° sun get large enough that ACES tonemapping stops
rolling them off and "half the picture blows out" — hence a gain factor patched
straight into the shader output.
- On the follow cameras, the sharpest one:
```js
/* A damped follow lags permanently by v/follow — at 105 cm/s that's more than
the nominal distance. The lead compensates, but is capped at 60% of the
nominal distance: without the cap it shoves the camera through the bee. */
```
That's a control-theory bug (a first-order lag has non-zero steady-state error under
constant velocity), its fix, and the fix's *own* failure mode, in three lines. You
don't write that from first principles. You write it after watching a camera drift
behind a bee, adding a lead term, and then watching the camera fly through the bee.
Here's where I have to be careful, because the honest answer is that I can't prove
that. The repository has two commits, 86 seconds apart, and the second one only edits
the README. There is no iteration visible in the history — the whole scene lands in a
single commit. So I can see the residue of a look-fix-look loop, but I can't
distinguish "it ran, looked, and fixed" from "it predicted these failure modes up
front." The git history simply doesn't record what happened inside the session. What
I can say is that the failure modes named in those comments are real, specific, and
mostly non-obvious, and that a codebase written blind does not usually document the
mistakes it isn't making.
## Then I ran it somewhere it had never run
Everything above is a judgement call. Here's the one part of this that turned into an
actual, checkable defect — and it only turned up because I did something the author
never did.
To get the pictures in this post, I rendered the scene headless: Chromium with no GPU
at all, WebGL2 on SwiftShader, a pure software rasteriser. That's a very different
stack from the browser this was built and looked at in. It booted, drew the meadow,
and logged this:
```
THREE.WebGLProgram: Shader Error 0 - VALIDATE_STATUS false
Material Type: ShaderMaterial
Program Info Log: Fragment shader is not compiled.
ERROR: 0:123: 'patch' : Illegal use of reserved word
```
`terrain.js` names a local variable `patch`:
```glsl
float patch = fbm(vWorld.xz * 0.012);
```
`patch` is a keyword in the tessellation stages of later GLSL ES versions, and the
shader translator in ANGLE — the layer Chrome puts between WebGL and whatever is
underneath — rejects it as reserved. The terrain material fails to link, and the
ground drops out of the scene.
I should be careful about what that proves. The rejection comes from ANGLE's
translator, not from the software rasteriser behind it, and Chrome routes WebGL
through ANGLE on Windows and macOS as well. So there are two possibilities and I can't
tell them apart from here: either the author's stack takes a path that accepts the
word, or the ground has never drawn for anyone and nobody noticed — which is entirely
plausible, because at 233,000 blades there is very little visible ground to miss. I
have no machine here that can settle it. Either way the fix is a one-word rename, and
either way *the word is not legal GLSL ES.* The pictures in this post were taken with
that rename applied locally; without it there is no ground under the grass.
The bug is trivial. Its shape is not:
What no amount of looking would find
It renders perfectly on the machine it was built on
The shader source looks completely ordinary
There is no test that could have been written for it
Reading 1,922 lines would not have flagged it
What found it in one run
A second renderer with a stricter compiler
Zero domain knowledge required
Total effort: run the thing somewhere else
The error message names the line and the word
This is [the CUTLASS post's argument](/blog/bumping-a-dependency-you-cant-read/) wearing
different clothes. There, the thing that made an unreadable change safe was an fp64
oracle. Here there's no oracle to build — but "the same input through a second,
independent implementation" still works, because a second GLSL compiler *is* a second
implementation. In a domain where correctness is undefined, the sliver of it that
remains defined is still worth harvesting, and it's cheap.
## What I did not verify
The usual section, and it's longer than normal this time.
Every frame in this post came out of a software rasteriser on a cloud VM, at the medium
quality preset (124,000 blades of a possible 233,000), running between 4 and 30 frames
per second depending on the setup, with the `patch` rename applied so the ground would
draw. So the pictures are dimmer, coarser and lower-resolution than the same scene on
a GPU, and they are not the artefact the author is describing. I have not seen this run
on the hardware it was built for. I have also taken no performance numbers at all — the
frame rates above say something about SwiftShader and nothing about the scene.
I also haven't verified the taste claims against anything but my own eye, which is the
entire point of the post and also its main weakness. "Backlight is the right decision"
is not a falsifiable statement. What *is* checkable, and what I did check, is that the
code does what the README says it does: the sun really is at 4.2°, four of the five
shots really do orient by `backlitAngle()`, the poppy really does carry the highest
transmission constant, and the blade count in the on-screen readout really is the sum
of the two grass instance counts for the active quality tier. The prose and the code
agree. That is a lower bar than "it's beautiful," but it's a bar a lot of README files
fail.
And I can't attribute the aesthetic decisions with any confidence. Three.js ships
examples, the training data contains a great deal of shader code, and "low sun, backlit
grass" is an established look, not an invention. What I'm claiming is narrower than
originality: that given an under-specified sentence, the budget went into art direction
and physical proportion rather than into feature count. That much is visible in the
diff.
## The principle
Where an oracle can exist, build it, and then you never have to read the code —
that's imp, and it's most of this site. What I didn't have a good answer for was the
other case, and this is the closest I've got to one.
When nothing downstream can be tested, the substitute isn't testing. It's picking a
small number of constraints early and hard enough that everything after them becomes
checkable *against those*. Fix the sun at 4.2°, and now every camera has a right and a
wrong side, every material owes a transmission term, and the palette has one saturated
accent instead of an argument. Fix one unit to one centimetre, and every size is
either the real one or a bug. None of that makes the scene beautiful. It makes it
*consistent*, and consistency is the part a machine can hold across 1,922 lines while
you look away.
The rest of it — whether a meadow in low sun was the right idea at all — is still
yours. It's just a much smaller job than it was.
The whole thing is [nineteen hundred lines in a public
repo](https://github.com/kekzl/bienchen), with no build step. Clone it, `docker compose
up`, and press `O` to take the camera off the director.