Skip to content

Ink on paper — ink_amount, paper_, ink_, ink_gamma

The last stage before present, and the only engine-wide one that is not per-preset: it remaps the one finished frame, which during a cross-preset dissolve is the blended frame of both presets. Your ink_*/paper_* values therefore crossfade with the dissolve rather than snapping — a switch from a white-paper preset into a black-ink one travels between the two poles. It reads each pixel’s brightness as an ink density and repaints the frame between two colours: paper where the frame was dark, ink where it was bright. The defaults are white paper and black ink, so ink_amount = "1" alone gives black marks on a white field — the “ink on paper” look. Works on any scene, sparse or full-screen, because it operates on the finished frame rather than a scene’s pipeline.

This is the one control that reaches a dark-on-light look at all. The scenes draw additively (a lightening model), so a dark stroke colour adds nothing and a light bg_bright just washes the strokes out — no combination of bg_* and palette colours gets there. The remap inverts the tone at the end instead.

Ink reads the frame after the tonemap, which is what it has always read — display-referred pixels in 0..1. So nothing in this section changed with Plan 0045’s linear light, with one practical consequence worth stating: exposure and bloom_amount are upstream of the remap, so raising either pushes more of the frame toward the ink pole. That is the lever for an ink preset whose drawing is coming out too faint, and it is a better one than widening the two poles — see the note below on why the poles do not do what they look like they do. It is not the lever for “the ink should bite harder” — that one is ink_gamma, and the three-lever note below says which does what.

ParamDefaultMeaning
ink_amount00 = off (passthrough). 1 = full remap. Bindable — try "beat" to snap into ink on each hit. Read the note below before resting it between the two.
paper_hue0Paper (dark-input) hue, into the HSV wheel; wraps, so it can sweep freely.
paper_sat0Paper saturation. 0 = neutral.
paper_bright1Paper brightness. 1 = white.
ink_hue0Ink (bright-input) hue.
ink_sat0Ink saturation. 0 = neutral.
ink_bright0Ink brightness. 0 = black.
ink_gamma1Response between the two poles — how fast a pixel travels from paper to ink. 1 is the identity. Above 1 thins the mid-tones toward paper, so only the strongest strokes keep full ink; below 1 inks the mids for a heavier, flatter print. Neither pole moves at any value. Bindable and continuous; clamped to 0.05 .. 20, both far outside anything a look wants.

These poles are LINEAR LIGHT, and a [palette] stop is not. A stop is sRGB and is decoded at load, so the hex you write is the colour that renders. paper_bright and ink_bright take no decode — they are the light itself, so a “dark” paper needs a far smaller number than the display value suggests: lsystem_vellum measured 0.07 as a mid violet and settled on 0.015 for a true near-black. The same is true of bg_bright. If a pole reads far brighter than the number you typed, this is why, and the fix is a smaller number rather than a different hue.

[params]
ink_amount = "1" # black on white - nothing else needed
# ...or a colored duotone: indigo ink on cream paper
paper_hue = "0.11"
paper_sat = "0.13"
paper_bright = "0.97"
ink_hue = "0.68"
ink_sat = "0.85"
ink_bright = "0.30"
# ...and hold the mids off the ink pole, leaving it to the strong strokes.
# The paper stays exactly where it was - see the three-lever note below.
ink_gamma = "2"

A partial ink_amount is a transition, not a resting value. The stage crossfades between the remapped frame and the untouched source, and the source is a near-black frame — so at 0.5 the paper is half black. That reads as a dirty page rather than a faint drawing, and the strokes lose contrast at the same time. Bind ink_amount when you want to travel between the glowing and the drawn look ("beat", or an eased ramp via [smoothing], both of which pass through the middle rather than sit in it); pick 0 or 1 when you want to stay somewhere. A sparse, faint drawing comes from the scene — a finer size, a shorter fade — not from a half-strength remap.

Three levers shape one response, and they are not interchangeable. An ink look that is “not contrasty enough” has three places to go, and picking the wrong one costs structure:

  • exposure moves the level going in. It sits upstream of the remap, so raising it pushes more of the frame toward the ink pole — and it moves the paper with it, because a brighter base is no longer near zero. Reach for it when the drawing itself is too faint.
  • ink_gamma reshapes the response between the poles, and only that. The paper does not move: a black pixel lands on paper and a full-brightness one lands on ink at every value — that is the exponent’s defining property, not a tuning — so raising it leaves less of the frame inked without lifting the page or dimming the strokes. This is the lever for “the dark pole should bite harder”.
  • ink_amount is how much remap happens at all, not how hard it bites. A partial value blends the remapped frame back toward the untouched source (see the note above), which greys the page. It is a transition, not a contrast control.

Measured on a 256-step grey ramp put through the remap with the default poles (white paper, black ink) — mean output byte over the ramp’s interior, higher = more paper:

ink_gamma0.250.5124
mean output byte147180209229241

Read the direction and size of the move, not the absolute: the ramp’s levels are sRGB bytes while the key is linear light, so a byte-50 % grey is already a low density and the identity row leans toward paper before anything is bound. The two ends of that ramp — pure black in, pure white in — came back as exactly the same two bytes at every one of those five exponents.

Which way you want to go depends on how dense the drawing already is, and the identity row above is the reason. Rendered on attractor_ink (a sparse, mid-density figure) at the same four values: 0.5 gives a heavier, sootier mark; 2 drops the fine tracery and leaves the strong strokes; 4 thins the whole figure to a ghost. So on a faint drawing, raising it separates the strokes and costs weight — pair it with exposure, or go below 1 for a heavier print — while on a drawing that is already crowding into a blot, raising it is what buys the page back. The cream paper was pixel-identical in all four.

Like the rest of the ink_* family, ink_gamma crossfades across a cross-preset dissolve rather than snapping.

Swapping the poles does not make a dark look on a continuous field. The pass is mix(paper, ink, luminance) — it interpolates, it does not map. Setting a dark paper against a bright ink is the obvious way to try to turn a print into a glow, and it works only where most pixels already sit near 0 or near 1: a line scene against black, not a continuous field. A source at mid luminance lands halfway between the poles no matter how far apart you set them. Measured: paper_bright = 0.055 against ink_bright = 0.94 on a developed Gray-Scott field renders flat slate grey. To darken a continuous field, change what the scene produces — exposure, contour density, the palette — not the poles.

In ink mode a scene’s palette collapses to the duotone. The remap keys on luminance only, so the hue a [palette] produced is discarded and every pixel lands somewhere on the paper→ink ramp. The palette still matters — it shapes which parts of the frame are bright, and therefore which parts become ink — but the two colours you actually see come from paper_*/ink_*. Reach for saturation and the palette to sculpt contrast in an ink preset, not colour.

The HUD, the browse overlay, and the diagnostics overlay draw after this stage, so they are never inverted.

Built from a8ce055 at version 0.115.0. This site tracks main and is not versioned per release.