The demo
Load any video of a person against a plain background. The demo extracts the foreground using a color-distance matte and flows article text around the moving silhouette — every frame, using pretext for line placement.
This demo runs in your browser and needs JavaScript. The text around it explains what it shows.
This demo runs in your browser and needs JavaScript. The text around it explains what it shows.
Best results: videos with a person against a solid or lightly textured background (studio green/white screen, plain wall). The sliders let you tune the wrap strength, subject scale, and position.
How it works
This demo is a compressed port of Matteflow by summerKK — an editorial layout prototype that demonstrates exactly why pretext exists. The original has four modules; here they’re ported into one React component and the small pure modules it draws with.
Step 1: color-distance matte
Each video frame is drawn to an offscreen canvas and the pixel data is read once:
offCtx.drawImage(video, 0, 0, canvasWidth, canvasHeight)
const { data } = offCtx.getImageData(0, 0, canvasWidth, canvasHeight)
The background color is estimated by sampling the frame edges (corners + borders). Then each pixel gets an alpha value based on its color distance from that background:
const distance = Math.sqrt((r - bg.r) ** 2 + (g - bg.g) ** 2 + (b - bg.b) ** 2)
const alpha = clamp((distance - threshold) / feather, 0, 1)
A pixel close to the background color gets alpha ≈ 0 (transparent). A pixel far from it gets alpha ≈ 1 (foreground). The demo fixes both numbers (threshold and feather are constants in the renderer), so its sliders do not touch the matte: Wrap sets how strongly the text bends around the silhouette, Scale sizes the figure, and V-Shift / H-Shift move it inside the frame.
Step 2: wrap profile
The alpha mask is divided into horizontal bands. For each band, the leftmost and rightmost foreground pixels define the subject width and horizontal center at that height:
// For each band (vertical slice of the figure)
const bandWidth = (rightPixel - leftPixel) / frameWidth
const bandCenter = (leftPixel + rightPixel) / 2 / frameWidth - 0.5
profile.push({ width: bandWidth, offset: bandCenter })
This profile — 10 numbers describing the silhouette shape — is computed every frame. It drives where the text exclusion zones are.
Step 3: text layout with pretext
For each text line, the wrap regions are computed from the profile:
const regions = computeWrapRegions({
y,
lineHeight,
stageCenterX,
stageWidth,
profile,
wrapStrength,
gutter,
articleLeft,
articleWidth,
stageTop,
stageHeight,
})
computeWrapRegions returns one or two column regions — the areas to the left and right of the subject that are wide enough for text. Then layoutNextLine from pretext places text into each region:
for (const region of regions) {
const line = prepared.nextLine(cursor, region.width)
if (!line) break
lines.push({ text: line.text, x: region.x, y })
cursor = line.end
}
This runs per frame. The expensive part — font measurement — happened once when the component mounted. The per-frame work is arithmetic: profile computation + line break logic. The result is text that genuinely follows the moving shape.
Why this matters beyond the demo
The Matteflow demo is not a real-world feature. But the pattern it demonstrates is:
Text layout as a pure function of content + geometry, evaluated at will.
When layout() costs 0.09 ms, you can call it in your render loop without a second thought. You can rebuild the entire text layout every frame in response to any change — container resize, content update, obstacle movement — and the user never notices the cost.
Without pretext, this demo would require either:
- Pre-computing layouts for every possible video frame (infeasible)
- Running DOM measurement off-thread (not possible; layout is main-thread only)
- Accepting the reflow cost and watching the frame rate drop (the wrong answer)
The library’s value is not just speed. It is that text measurement becomes a value you compute — not a side effect you wait for.
This piece is based on the pretext README and Cheng Lou’s original library. The Matteflow concept is from summerKK/Matteflow.