WindField

The wind on a weather map, combed into streaks that travel along the flow, with the speed read as colour.

the wind runs on its own

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

Options

ground #0d141a

css colour

The ground, and what shows where there is no wind. Dark, because the streaks are added onto it: that is what lets them cross each other without going muddy, and it is also why this item does not work on paper without inverting the idea.

calm #3f6f93

css colour

The slowest air, at the bottom of the ramp. Cool and close to the ground, so the still parts of the field recede and the fast parts are what you look at.

mid #58a58c

css colour

The middle of the ramp. It exists so the ramp is read by hue rather than by lightness: a straight line from calm to strong gives one hue and a tint of it, which is the look of a gradient rather than of a chart.

strong #d8a04a

css colour

The fastest air, which in practice means the air close in around a system. Warm, because that is the convention every wind map uses and conventions are worth keeping where they cost nothing.

systems 4

1 to 8, good: 3 to 5

How many pressure systems are in the field. Few and large reads as weather; many and small reads as decoration, because a real chart is mostly one system's skirt running into another's. This is also the main cost: the field is evaluated eighteen times per pixel and each evaluation walks this many systems.

drift 0.42

0 to 1.5, good: 0.2 to 0.6

The prevailing wind across the frame, before any system touches it. At 0 the systems sit in dead air and the field has no direction, which is the single clearest way to make this look like a screensaver.

wander 0.06

0 to 0.3

How far each system travels from its home over a period, as a share of the height. Small: weather moves, but a backdrop that moves its structure around is a backdrop you keep looking at. At 0 the field is fixed and only the streaks travel, which is a legitimate and very quiet way to use this.

scale 2.2

0.8 to 12 px, good: 1.2 to 3

Cell size of the noise the streaks are made of. It sets how coarse the combing is, and nothing else in here does. Large values give a few wide ribbons; small ones give fine hair, and below about 3 it starts to alias into sparkle.

length 60

10 to 180 px, good: 40 to 90

How long a streak is. This is the distance the walk covers, so it is also how far the shader can see along the flow: long streaks follow a curve further and make the field legible, and past about 120 they start to straighten the tight air near a system centre into chords.

turns 3

1 to 12, good: 2 to 5

Whole turns the streaks travel along the flow in one period. Whole, because a fraction means the loop never comes home, and core.ts rounds it rather than trusting it. With the default period of 36 seconds, 3 is a streak pattern that takes twelve seconds to repeat.

contrast 0.46

0 to 1, good: 0.35 to 0.7

How hard the result is stretched away from its mean. Line integral convolution of a uniform field sits in a narrow band around the middle, so at 0 this is a flat wash with a hint of structure. Past about 0.8 the streaks go to hard black and white and the field stops reading as air.

seed 11

0 to 9999

Seed for the systems: where they sit, which way they turn, how big and how strong. The same seed gives the same weather, so a recording and a live page agree streak for streak.

period 24

10 to 180 s, good: 24 to 90

Seconds for one cycle. Long, because a backdrop has to survive being ignored: this is both how long a system takes to go round its home and the time in which the streaks make `turns` complete passes.

What it needs

Category
Backdrops, an effect
Dependencies
None at all
Browser
WEBGL2
Frame budget
~2 ms · 1 context
Pinned to
v0.13.0
  • Line integral convolution, not a particle system. Each pixel walks a short way up the flow and a short way down it, samples a fixed noise field at each step, and sums those samples under a travelling kernel: along a streamline they agree and the pixel is bright, across the flow they disagree and cancel
  • That is not a stylistic choice. A particle's position depends on the one before it, so an advected field cannot be seeked, cannot be recorded, drifts on a dropped frame, and is wrong on the first frame after a resize, because the trail is feedback on the canvas. The page this was taken from runs fourteen thousand particles with a 0.07 erase per frame and there is no value of t you can ask it for
  • The streaks move because the kernel's phase moves, not because anything in the field is advected. `turns` is a whole number of turns over the period and is rounded in core.ts rather than trusted, because it is the one value that can stop the loop closing and it arrives from a prompt
  • Each system orbits its own home once per period, so the weather changes without the loop failing to come home
  • Field space is y from 0 to 1 and x across the aspect ratio, rather than uv. A system placed in uv is an ellipse on a wide canvas
  • Colour is a legend rather than a palette. Speed picks the hue through two interpolations, because a straight line between two colours gives one hue and a tint of it, and a chart like this is read by hue
  • Built for a dark ground and declared as one. The streaks are drawn by addition onto the ground, which is what lets them overlap without going muddy, and on paper that makes them brighter than the paper rather than darker
  • Four systems by default rather than eight. Eight small vortices read as a decorative swirl; four large overlapping ones read as a chart, because a real synoptic map is mostly one system's skirt running into another's
  • One WebGL2 context, one full-screen triangle, no buffers and no attributes
  • Seven field evaluations each way, each followed by four noise samples taken in a straight line along the direction that evaluation gave. The field is what costs, and a streamline is locally straight over a couple of pixels, so the samples that make the combing are nearly free
  • The step has to be about one noise cell. Longer and consecutive samples are independent, which is static; much shorter and they are identical, which is smear. The first version stepped 7px through 7px cells and produced neither: it read as wet paint
  • Measured with `pnpm budget` at 1280 by 720: 2.0ms for a draw and a forced flush, against DustMotes at 1.6 on the same run. The comparison is the part to trust rather than the absolute: DustMotes read 0.7 earlier in the same session on the same machine, so these numbers drift with whatever else is running and are only worth anything beside a reference taken at the same time
  • 0.104 mean absolute change per frame on a 0 to 255 scale, worst 0.125, sampled at six phases. It was 0.044 at a 36 second period; halving the period to 24 to get the take inside its media budget doubled it, which is the trade and is still inside the band a backdrop wants
  • maxDpr is capped at 1.5 rather than the usual 2. What it draws is noise at a cell of about two pixels, so the fourth sub-pixel is paid for and not seen
  • No textures, no buffers and no feedback. There is nothing to allocate between frames
  • A field of fine hard-edged noise is the case a codec has nothing to work with: there is no redundancy in it, so the settings that are generous for a photograph are extravagant here. The take is 24s at crf 40 and webmCrf 54 for that reason, and the first attempt at 36s and crf 34 came out 3832 kB against a 3500 limit

Concept credit: the field of named pressure systems behind the Wind Field Routing page, and the speed-as-colour ramp that goes with it. The method here is line integral convolution rather than fourteen thousand advected particles, because an advected field cannot be seeked, by Ray Velez. The implementation here is written from scratch.