Algorithms

Every way this library knows of splitting one image across frames, run on the same content and scored against three different denoisers. Two of them work by different mechanisms, two fail, and the failures are kept so they can be run rather than asserted.

leak is |correlation| between one captured frame and the truth 0.00 perfect ยท 1.00 fully readable
A correction, and the metric is the thing that was wrong. This page said aperture was the calm option, on the strength of 21.6 modulation against amplitude's 94.2. Looked at on a real panel it is the worst of them, and the numbers never disagreed with that. They were incomplete.

Modulation here is measured per frame boundary and says nothing about how often those boundaries arrive. Aperture needs six frames per cycle where amplitude needs two, so on a 60Hz display its cycle runs at 10Hz against amplitude's 30Hz. Around 8 to 15Hz is where the eye's temporal sensitivity peaks, so aperture puts a smaller modulation at the worst possible rate and amplitude puts a larger one near fusion. Smaller steps, far more visible.

So the comfort axis needs frequency in it, not just amplitude, and until it does these numbers rank configurations only when the frame count is equal. Aperture remains worth having for the case it was built for, a palette with no headroom at all, but it is not the comfortable option and this page should not have said so on one number.

Both axes, on one page

These used to be two pages, which was a mistake: they measure different things about the same object. Leak is what an attacker gets. Modulation is what a viewer pays. Every mode has both, and a mode is only worth shipping if you have looked at both.

Within-pair modulation is the flicker between the two frames of a cycle. It carries the masking and cannot be lowered without lowering the protection. Across-cycle is the flicker between one cycle and the next. It carries nothing, and every code level of it is churn for no security at all.

No comfort score, deliberately. Producing one means inventing coefficients for a spatiotemporal contrast sensitivity function, and an invented coefficient inside a metric is worse than no metric because it survives review by looking quantitative. Modulation is mechanical. Whether a given amount is tolerable needs your eyes on a real panel, which is what the live panels are for.

Leaning the noise toward the ink

Flicker over empty background protects nothing, so ink-bias lets the amplitude lean toward where the content is. It is the largest comfort win available and it comes straight out of the protection, because where the noise is, is where the text is. Measured over 6 seeds on the shipped palette:

ink-biasleak after the best blurwithin-pair modulation
0, ships0.26396.5
0.20.30481.4
0.40.36166.4
0.60.44151.4
1.00.73221.4

Around 0.2 buys roughly 16% less flicker for a leak still under a third. Past 0.4 the value starts coming back and by 1.0 it is plainly readable, which is the trap arriving exactly as predicted rather than as a surprise.

The map is blurred well past the stroke width and keeps a floor, so it cannot follow a glyph edge and the background is never fully quiet. That stops the amplitude map outlining the text. It does not stop the trade, which is inherent: quieter background means the noise is concentrated where the signal is, and concentration is information. Default 0, and measure on your own content before going past 0.3.

What the merged page got wrong first

aperture is far calmer than amplitude, and neither page could have told you that on its own. Measured on the panels above at the shipped block:

modeleakwithin-pairacross-cycle
amplitude0.24894.247.6
aperture0.14821.621.6

A quarter of the modulation, and it reads far worse. The number is per frame boundary and aperture has three times as many of them per cycle, so what looks like a calmer field is a smaller step delivered at 10Hz instead of 30Hz. The table above is only comparable between modes with the same frame count, which is a limitation of the measurement rather than a property of the modes.

Note also that aperture has within-pair equal to across-cycle. Its frame content is deterministic rather than redrawn each cycle, so consecutive frames differ by the same amount whether or not a cycle boundary sits between them. amplitude is the only mode here where the two numbers come apart, and that gap is the churn worth attacking.

Two mechanisms, not one

amplitude and aperture hide in genuinely different ways, and the difference decides which one you want.

amplitude degrades everything. Every pixel is in every frame at the wrong value. Nothing is missing and nothing is right, and how wrong it can be depends on how much headroom the colours have. White has none, which is why fit exists.

aperture withholds most of it. One slice per frame, carried perfectly, the rest genuinely absent from the file. It does not care about colour at all, so it works at pure white where amplitude cannot. The cost is that what it does show, it shows exactly, and a burst of N frames has everything.

So: amplitude against a single screenshot on a palette with room, aperture when the palette has none and you can accept the flicker of a sweeping band.

Why the other two fail

The three that fail all split a dimension the eye does not need in order to recognise something. Recognition survives losing colour, losing most of the pixels, blur, and heavy quantisation. What it does not survive is having the pixel values randomised, which is the one thing amplitude does and the others do not.

That is worth stating as a rule, because the failures are the tempting ideas. Splitting colour across frames sounds like it should hide something. It hides nothing: one frame is a greyscale copy at full spatial resolution. Splitting pixels across frames sounds stronger still, and gives you a copy at half resolution, which reads fine.

Where the score itself fails

leakScore is the absolute Pearson correlation between one frame and the truth, and it is a proxy rather than the thing itself. It has a blind spot worth knowing about, because this page walked straight into it.

A mode that inserts a large pattern unrelated to the content, as interleave does with its flat fill, gets a better score for it. The fill adds variance that correlates with nothing, the correlation is diluted, and the number improves while the frame stays perfectly readable. The first version of this page reported 0.19 for interleave and called it masked, next to frames you can read at a glance.

The lesson generalises past this one mode: a metric that disagrees with your eyes is wrong until proven otherwise. The frames are shown above every score on this page for that reason.

About the attacks

Every blur-resistance figure in this project used to be measured against a box blur, which is the cheapest denoiser there is. That is a weaker claim than it sounds, so the stronger ones are here and run on every algorithm.

The box blur wins anyway. At the shipped defaults nothing beats not attacking at all, and the ordering holds in the under-protected case where the block is half the stroke. The scores above are computed live on this page, so read them there rather than here.

The median is the interesting failure. It is the standard tool for impulsive noise and it preserves edges, which ought to be exactly right against blocks over strokes. It loses for a structural reason: at a radius small enough to keep the strokes, the window fits inside a single noise block and finds no outliers to reject. At a radius wide enough to see across blocks it destroys the strokes too. The block size defeats it the same way it defeats a blur.