Explainer · The algorithm

How Bandaid finds and removes the bands

There's no machine-learning model and no training data. Bandaid is a chain of signal-processing steps that use one weakness of flicker banding: it repeats at a steady rhythm along one direction, and real scene detail almost never does. Every demo on this page runs the same code as the app.

12 min read4 live demosRuns in your browser
LineariseUndo the camera's brightness curve and take logs, so a band becomes an offset.
Line mediansTake the median row-to-row change in 16 zones. Scene detail drops out.
Find the rhythmLook in the spectrum for a sharp, widespread peak. Score how confident that is.
DemodulateTrack band strength, phase and shape locally, like a lock-in amplifier.
LiftRaise each line to the local bright-band level, per colour channel.
ApplyScale up and correct the full-resolution file. Keep the camera metadata.

01Turning a multiplier into an offset

As the physics article shows, a banded photo is the true photo times a gain that depends only on the row and the colour channel. Two transformations make that easier to work with.

First, undo the camera's brightness curve. JPEG values are gamma-encoded, so a 50% brightness drop doesn't halve the pixel number. Bandaid converts every pixel to linear light with the sRGB formula, where light really does add and multiply.

Second, take the logarithm. Logs turn multiplication into addition:

log observed(x, y) = log true(x, y) + log gain(y) // the band term is identical for every pixel in row y

Bandaid also sets two kinds of pixel aside: very dark ones, where JPEG noise swamps any signal, and clipped highlights, where the true value is unknown. Those carry no band information.

02Separating bands from the picture

Compare each row with the row above it. Every pixel's change is the scene's change plus the band's change, and the band's part is the same everywhere along the row. Most of the scene changes very little from one row to the next. The exceptions are edges: a shoulder, a table top, the bottom of a letter.

An average of those changes gets dragged around by every edge. The median (the middle value) ignores them as long as fewer than half the pixels in the row are on an edge. Bandaid also throws out any change bigger than about half a stop: a real band edge is spread over several rows, while scene edges are sudden. Add the medians up row by row and you get the band profile.

Demo 1 · Median vs averageLive
Banded test image
Recovered band profile, down the frame
Estimator
Error vs true profile
–
Grey is the true band profile used to make the test image. Cyan is what the chosen estimator recovers. With Average, the faces, lettering and table edges leave steps and spikes. The median follows the bands and ignores them.

Bandaid computes these medians separately in 16 vertical strips (zones) across the frame. That gives the detector 16 independent views of the same bands, and lets the correction vary from one side of the frame to the other.

03Finding the rhythm

Most photos have some structure from row to row. What makes banding stand out is that it repeats at a fixed spacing. So Bandaid takes a Fourier transform of each zone's row-to-row changes: a breakdown of the signal into repeating waves at every possible spacing.

Natural images put most of their energy into slow changes. Using row-to-row differences rather than raw brightness flattens that out, so a steady band rhythm stands out as a sharp spike. Bandaid scores the spike in two ways:

  • Prominence: how far the spike rises above the surrounding spectrum, measured as the median of nearby frequencies.
  • Coverage: how many of the 16 zones show their own spike at the same spacing. Real banding covers wide areas. A striped shirt or a railing that happens to repeat is usually confined to a few zones.

The zones are combined by their power alone, ignoring where in its cycle each zone's pattern is. That matters for LED walls, where tiles band in step with each other's rhythm but not with each other's timing. Confidence is a smooth function of prominence and coverage. Below about 50%, the app treats the photo as clean and leaves it untouched unless you force the fix.

Demo 2 · The detectorReal algorithm
Test image
Spectrum · spacing in px (log scale)
Spacing found
–
Prominence
–
Coverage
–
Confidence
–
Drag the depth to 0 and the spike disappears into the floor, so confidence falls to zero. Even at 0.1 stops, which is hard to see by eye, the spike usually stands far above the noise. The dotted lines are harmonics.

Harmonics, and picking the true spacing

Sharp-edged bands aren't smooth waves. Like a square wave, they contain the main rhythm plus echoes at ½, ⅓ and ¼ of its spacing. Taking differences boosts those high-frequency echoes, so the tallest spike is sometimes an echo. Bandaid checks for the lowest clear spike that the tallest one is a whole-number multiple of, and uses that as the true spacing.

It also runs the whole detection on the photo rotated 90°. A portrait shot from a landscape-oriented sensor bands left to right, and the direction that scores higher wins.

04Measuring the bands locally

Knowing the spacing isn't enough. To divide out the bands, Bandaid needs their exact shape and position everywhere in the frame. Two steps get it there.

Fine-tuning the spacing. The spectrum gives the spacing to within a few percent. Over 60 or more bands, a 1% error drifts the pattern by more than half a band. Bandaid stacks every row onto a single cycle at a candidate spacing, for each zone. The right spacing gives the crispest stacked shape; a wrong one smears it. It tries 81 candidates and keeps the sharpest.

Lock-in demodulation. This is the trick borrowed from lab instruments. To measure a weak signal at a known frequency, you multiply it by a sine and a cosine at that frequency and then smooth the results heavily. Everything at other frequencies averages to nothing. What survives is the local strength and timing of the band rhythm. Bandaid does this per zone, for the main rhythm and up to 12 harmonics, so it captures sharp band shapes as well. Smoothing over about one band means it can follow changes, like a tile boundary, within a band or two.

Demo 3 · One shape for the frame vs local demodulationLive
Left after one-shape fix
–
Left after local demodulation
–
Top: the band signal along a line that crosses from one LED tile to another halfway down, where the timing jumps. Middle: what's left after fitting one band shape to the whole line, which is roughly how Bandaid's first version worked. Bottom: what's left after local demodulation. This change took the Cheltenham backdrop from about 25% of the banding remaining to noise level.

05Choosing what “correct” means

The measured band signal says how much each row was darkened relative to its neighbours, but not relative to what. Bandaid offers two reference points:

  • Brightest (default): the rows that caught the whole light pulse are the true exposure. Bandaid finds the local bright-band level using the 80th percentile over one band cycle, rather than the maximum, so noise and overshoot don't inflate it. It then lifts every row up to that level.
  • Average: flattens the bands around their local mean. The overall exposure stays the same, with the dark rows lifted and the bright rows lowered equally. This is useful if the brightest version looks too hot.

The red, green and blue corrections are computed separately, which is how magenta and green tints get removed along with the brightness bands.

06Mopping up, and a second light source

After the first correction, Bandaid measures again on the corrected image and adds any leftover, so whatever the first pass missed gets a second chance. Then it re-runs the detector on the result. If a different clear rhythm shows up, such as a second fixture on another dimming frequency, it removes that too, for up to three light sources. This is what cleared the 8-pixel leftover on one of the festival photos.

07Applying it to the full-resolution file

Analysis runs on a copy at most 3,000 pixels on its longest side. That keeps it fast and memory-safe on a 45-megapixel file, and bands are smooth enough that nothing is lost. The corrections are stored as smooth curves per zone and per colour. On export, Bandaid scales them to the original size, blends between neighbouring zones, and multiplies every pixel in linear light, using lookup tables so a large file takes about a second.

The camera's EXIF block (lens, settings, capture time, copyright) is copied into the new JPEG, with the orientation tag reset because the pixels have already been rotated upright. Everything happens in your browser; the photo never leaves your machine.

Demo 4 · The whole pipelineReal algorithm
OriginalFixed
Match bands to
Banding before
–
Banding after
–
Spacing
–
Time
–
A synthetic panel talk with tiles that each band on their own timing, corrected live. Drag across to compare. Set Local zones to 1 to see why one correction for the whole width fails on independent tiles. Banding is the RMS difference from the clean image in stops, ignoring any overall brightness shift.

08Where it struggles

  • Very faint, very local bands. A light band on a small patch of backdrop can sit below the detector's threshold, because loosening it would also start "fixing" clean photos. Use Fix even if no bands are detected for those.
  • Things in front of a banded wall. The correction is smooth across each zone, so a face in the same strip as a heavily banded wall can pick up a faint lift. Real lighting usually bands the subject too, which hides this.
  • Lost detail. Rows that caught almost no light are mostly noise. Brightening them brightens the noise too, and clipped highlights can't be rebuilt.
  • Real stripes. Blinds, railings or a striped shirt filling the frame at a steady spacing can look like bands. The coverage check usually catches them.
How well it worksOn synthetic tests with known answers, Bandaid removes 85–95% of the banding error, finds the band spacing to within 1%, and leaves clean images unchanged. On the real festival photos, the remaining banding energy drops to the level of sensor noise.