The film movement of a 35mm projector, drawn as a service-manual elevation: a strip running down through a gate, a Maltese cross pulling it one frame at a time, and a shutter disc sweeping blades across the lit aperture.
A projector does not slide film past a lamp. It stops each frame dead in the gate, shows it, and then yanks the strip down by exactly one frame pitch. Everything else in the machine — and everything in this piece — is a consequence of that.
The pulldown
The yank is the job of a Geneva movement: a crank with a single pin, and a slotted cross on the intermittent sprocket’s shaft. The pin enters a slot, accelerates the cross from a dead stop, decelerates it, and leaves; a locking cam holds the cross rigid the rest of the way round. For an n-slot cross the pin is engaged while the crank turns 180° − 360°/n, and the cross advances 360°/n.
A four-slot cross therefore spends exactly a quarter of every frame period in
motion and three quarters standing still. That is not a number the module
approximates — crossAngle is the real relation,
β = atan2( sin α , 1/sin(π/n) − cos α )
clamped to ±180/n outside engagement, and the film position is
(β + 180/n) / (360/n) of a pitch. Which is why pulldown is one of the
loops: at 3, 4 and 6 slots the same code gives you a snatch, a shove and an
amble, and the film in the gate moves accordingly.
The first blade
That quarter of a revolution is a problem, because during it the picture in the gate is sliding. So the shutter — on the same one-revolution-per-frame shaft — carries a blade wide enough to be over the aperture for the whole pulldown. Hence the first constraint:
blade width ≥ pulldown arc + the aperture’s own angular width
The second term is the one nobody mentions. The aperture is not a point; it subtends a real angle at the shutter’s axis, so the blade has to be wider than the pulldown by that much just to finish covering the frame before the film starts to move. Cut it narrower and you get travel ghost: the frame moves while lit, and smears. The demo’s fourth readout is exactly this, and dragging the blade slider below the minimum puts red under the light curve and a smear in the averaged screen.
The second blade
Cover the pulldown and you have a working projector that flickers horribly, because you are flashing the screen at the frame rate and 24 Hz is nowhere near the fusion threshold — about 48 Hz at cinema brightness, and it climbs with brightness rather than sitting still.
The fix, and the title, is a second blade: an extra opaque sector that interrupts the same still frame a second time. It adds no information whatsoever. It doubles the flash rate to 48 Hz and the flicker goes away.
It is not free, and the piece is really about the bill. The eye integrates — a flash it cannot resolve is worth its time-average and nothing more (Talbot–Plateau) — so what the audience sees is the open fraction of the disc. On this geometry that is 68% of the lamp with one blade and 37% with two. The second blade buys steadiness with nearly half the light, in a currency nobody in the auditorium can see either half of.
The demo puts those two facts side by side deliberately: on screen, this instant is the aperture right now, and what the eye integrates is the same frame averaged over the window from one pulldown to the next. Add a blade and the right-hand panel gets dimmer while the left-hand one keeps flashing at full lamp brightness. That gap is the whole trade.
Where the two constraints collide
With equal blades you need n_blades × width ≤ 360, and width ≥ pulldown + subtense. A four-slot cross pulls down in 90°, so two blades is about all the
disc will hold. Ask for three and the aperture is dark 95% of the time.
Switch the cross to three slots and the pulldown is 60° — a nastier jerk, worse on the perforations, which is why nobody used it for a long feature — and only then does a third blade fit, at 72 Hz and 30% transmission. That is the silent era’s problem and the silent era’s answer: 16 fps needs three flashes per frame to clear fusion, and it can only have them because the movement is quick enough to hide behind a narrower blade.
How many times you can flash the screen is set by how fast the film can be yanked. That is the sentence the piece exists for.
Honest about the geometry
This shutter is small relative to its aperture — an 84-pixel canvas will only
hold so much disc — so the aperture subtends about 23° here against roughly 11°
on a real machine, where the disc is ten-odd aperture-widths across. The blades
in this piece are correspondingly wide (114° against a real ~100°) and the
two-blade transmission correspondingly mean (37% against a real 45–50%). The
relations are the machine’s; the constants are this drawing’s, and the demo
computes them from the pixels rather than quoting them. AP_SUBTENSE is
measured off the aperture’s own corners at load.
The drawing is an elevation, not a section: the film is face-on and the mechanism is laid out beside it, which is how a service manual draws a movement and not how the light actually travels.
The loops
- run — one frame period at two blades. The cross indexes, the film drops a pitch behind the closed master blade, and the second blade cuts the same still frame in half on the way past.
- single — the same cycle with the second blade removed. Brighter, and every flash is a new frame.
- pulldown — the index alone, with the shutter taken off, because at two blades every frame of it is black. This is the quarter of the cycle the audience is specifically not allowed to see.
Reuse
export/movement-<loop>-<n>.png— 1× frames (84×54, transparent background). Render at integer scales with nearest-neighbour filtering.export/movement-sheet.png— every frame in one strip, in loop order.export/movement@6x.png— prescaled hero, mid-cut.
Source
No .aseprite — the canonical source is source/two-blades.mjs, which is both
the kinematics and the pixel drawing. Nothing is keyframed: every frame is
renderFrame({ t, frame, blades, bladeW, slots }) evaluated at a phase, and the
film position is quantised to whole pixels because a strip that lands between
rows is mush rather than a picture. Regenerate everything with:
node source/render.mjs # export/, media/ loops, thumb, demo copy
node scripts/screenshot-demo.mjs # media/ demo shots
(needs site/node_modules installed — the scripts resolve Chromium through
site/scripts/lib/chromium.mjs.)
validateGeometry() is the cheap guard, and both scripts run it: it checks that
each cross indexes a full step, that the film advance starts at 0 and finishes
at 1, and that the minimum blade really does cover the pulldown. Change the
layout constants and it will tell you if the drawing has stopped being a
mechanism.