Type · effect

InkBleedText

Type on paper too absorbent for it, the ink wicking along the fibres.

ambient, watch the ink creep

Paste into Claude Code, Cursor or any agent. Pinned to a tag, so it will not change under you.

Options

bleed 3.4

0 to 14

How far the ink creeps, in pixels, at its furthest. Past about 8 the counters of a and e close up and the word stops being readable, which is a real thing badly printed paper does and rarely a thing you want.

floor 1.1

0 to 8

How far it has crept at its least. Deliberately not zero: a sheet that has taken ink does not give it back, so the letter never returns to a clean edge.

fibre 0.035

0.002 to 0.2

Coarseness of the fibre, as a turbulence base frequency. Lower is a longer, rougher, more absorbent fibre. This is the one to reach for if the bleed looks like noise rather than paper.

detail 3

1 to 5

Layers of fibre. More is a finer, more tangled structure and more work for the filter; the gain above four is hard to see.

period 9

1 to 60

Seconds for one breath of the ink. Driven by a cosine, so it is exactly periodic and the loop closes.

seed 4

0 to 999

Which sheet of paper. Any two seeds give different fibre; the same seed always gives the same sheet.

What it needs

Dependencies
None at all
Browser
No WebGL
Frame budget
~1.5 ms · 0 context
Pinned to
v0.11.0
  • No canvas and no WebGL. One SVG displacement filter, which is the honest mechanism: turbulence supplies the fibre and feDisplacementMap pushes each pixel of the glyph by the amount of fibre under it
  • Not a blur. A Gaussian softens an edge evenly, which is a lens out of focus rather than ink spreading, and it is what makes most attempts at this look photographic rather than printed
  • fractalNoise rather than turbulence: turbulence takes the modulus of each octave and leaves hard creases that read as cracks in the letter
  • The filter region is widened to 150%, because displaced pixels outside it are cut off and a heavily bled letter would come back with its edges sliced square
  • Each instance gets its own filter, so two bled headings on a page do not share one
  • Holds 59.8fps at 1440 by 900 at 2x DPR, measured, which is indistinguishable from the same page with no filter on it
  • That is only true because the turbulence is cached. It is the expensive half, and the fibre of a sheet does not change between frames, so only the displacement scale is set per frame. Setting baseFrequency every frame instead costs a full filter rebuild each time
  • SVG filters are rasterised on the CPU in some browsers. Use it on a heading, not on a page of body text, and expect the cost to scale with the filtered area