{"schemaVersion":1,"engineVersion":"4.0.5","generatedAt":"2026-10-23T21:17:01.196Z","groups":[{"id":"define","title":"Defining a shader","categories":["define","values","slots","invoke","lists"]},{"id":"paint","title":"Painting","categories":["paint","gradients","patterns","figures","compose","media"]},{"id":"light","title":"Noise, light & materials","categories":["noise","light","materials","volume","radiance","voxels"]},{"id":"space","title":"Shapes, frames & warps","categories":["shape","frames","warps","mask","math","motion","signal"]},{"id":"effects","title":"Filters & effects","categories":["filter","effects.color","effects.blurs","effects.stylize","effects.lens","effects.reveal","effects.instances","effects.fracture","effects.edgeGlow","effects.overlay","effects.pointerFields"]},{"id":"simulation","title":"Simulation","categories":["simulate","sim.grids","sim.fluids","sim.feedback","sim.agents","sim.agentForces","sim.agentFrame","sim.agentRender","sim.shapeFields"]}],"categories":{"define":{"id":"define","title":"defineShader & wgsl","group":"define","module":"std/lower.ts","access":{"kind":"top-level"},"curated":true,"words":["define.defineShader","define.wgsl","define.registerShader","define.unregisterShader","define.getRegisteredShader","define.getRegisteredShaders","define.onShaderRegistered","define.WgslBody"],"types":["define.DisplaceByEffectRef","define.GatherEffect","define.IdentityRule","define.PointwiseEffect","define.RecompileRule","define.StdCustomDefinition","define.StdDefinition","define.StdDefinitionBase","define.StdGatherFilterDefinition","define.StdGeneratorDefinition","define.StdPointwiseFilterDefinition","define.StdPropConfig","define.StdProps","define.StdShapeDefinition","define.StdWarpDefinition","define.StdWgslFilterDefinition","define.WgslHost","define.WgslInput","define.WgslSpec","define.WgslType"],"intro":"A shader is a plain object: a name, the props a user can set, and one field that says what it\ndraws. Hand it to `defineShader` and it becomes a component you can mount, nest, blend, mask\nand animate like any shader in the library.\n\n```ts\nimport {defineShader, transformColor, transformPosition, wgsl} from 'shaders/std'\n\nexport const Halo = defineShader({\n  name: 'Halo',                      // what the editor and preset JSON call it\n  props: {                           // the controls a user can set\n    color: {default: '#ffd166', transform: transformColor},\n    center: {default: {x: 0.5, y: 0.5}, transform: transformPosition},\n    radius: {default: 0.6},\n  },\n  paint: wgsl`                       // what it draws: a color for every pixel\n    let d = length((uv - center) * vec2f(aspect, 1.0)) / radius;\n    return vec4f(color.rgb, 1.0 - smoothstep(0.8, 1.0, d));\n  `,\n})\n```\n\n**One field says what kind of shader it is.** Give exactly one.\n\n| Field | You get | What is inside it |\n|---|---|---|\n| `paint:` | a generator that paints from coordinates (gradients, noise, light) | nothing: it stands on its own |\n| `effect:` | a filter over the layer nested inside it (tints, blurs, ripples) | the child layer, read as `child` or sampled as `childTexture` |\n| `map:` | a distortion that moves the pixels of the layer inside it | the child layer |\n| `shape:` | a 2D shape with fill and stroke | nothing |\n\n**Two ways to write what it draws.** Compose it from std words (the rest of this reference)\nand the engine compiles the composition to WGSL. A radial gradient between two color props is\none line:\n\n```ts\npaint: rampOver(dist.radial({center: p('center'), radius: p('radius'), aspect: 1, skew: 0}), pair(p('inner'), p('outer'), p('colorSpace')))\n```\n\nOr write the math yourself in a `wgsl` body: one function that returns a `vec4f` color. Your\nprops, `uv`, `time`, `aspect`, `viewport`, `pointer` and the child are bound by name, so there\nis no setup to write. A filter that inverts whatever is inside it:\n\n```ts\neffect: wgsl`\n  return vec4f(1.0 - child.rgb, child.a);\n`\n```\n\n**Props are the controls.** Each has a `default`, usually a `transform` that says what kind of\nvalue it is (`transformColor`, `transformPosition`, …), and optional `ui` metadata that tells\nthe editor how to show it. Blend modes, opacity, masks, transforms, dynamic props and code\nexport come with every shader; you never write them.\n\n**Use it like any other shader.** Mount it with `<CustomShader src={Halo}>` in React, Vue,\nSvelte or Solid. Call `registerShader(Halo)` when preset JSON should be able to name it as\n`type: 'Halo'`.\n\n```tsx\nimport {Shader, CustomShader, Blur} from 'shaders/react' // or shaders/vue, shaders/svelte, shaders/solid\nimport {Halo} from './halo'\n\n<Shader>\n  <Blur intensity={8}>\n    <CustomShader src={Halo} radius={0.8} />\n  </Blur>\n</Shader>\n```","example":"import {defineShader, wgsl, transformColor, transformPosition} from 'shaders/std'\n\n// A generator: concentric color bands radiating from a draggable point, on its own clock.\nexport const Halo = defineShader({\n  name: 'Halo',\n  description: 'Concentric color bands radiating from a point.',\n  animatedTime: {speed: 'speed'},\n  props: {\n    inner: {default: '#ffd166', transform: transformColor, ui: {type: 'color', label: 'Inner'}},\n    outer: {default: '#0b132b', transform: transformColor, ui: {type: 'color', label: 'Outer'}},\n    center: {default: {x: 0.5, y: 0.5}, transform: transformPosition, ui: {type: 'position', label: 'Center'}},\n    radius: {default: 0.6, ui: {type: 'range', min: 0.1, max: 1.5, step: 0.01, label: 'Radius'}},\n    bands: {default: 4, ui: {type: 'range', min: 1, max: 16, step: 1, label: 'Bands'}},\n    speed: {default: 1, ui: {type: 'range', min: 0, max: 4, step: 0.1, label: 'Speed'}},\n  },\n  paint: wgsl`\n    let q = (uv - center) * vec2f(aspect, 1.0);\n    let d = length(q) / radius;\n    let wave = 0.5 + 0.5 * cos(d * bands * 6.2831853 - time * 2.0);\n    let fade = 1.0 - smoothstep(0.7, 1.0, d);\n    return vec4f(mix(outer.rgb, inner.rgb, wave * fade), 1.0);\n  `,\n})","reach":[{"when":"a shader of your own, from a plain object","use":"`defineShader`"},{"when":"per-pixel math you want to write by hand","use":"`wgsl` in `paint:` or `effect:`"},{"when":"a generator that paints from coordinates","use":"`defineShader` with `paint:`"},{"when":"a filter over the layer nested inside","use":"`defineShader` with `effect:` holding a color word, `tintToward`, or a `wgsl` body that reads child"},{"when":"a filter that reads neighbouring pixels (blur, ripple, mosaic)","use":"a `wgsl` body that samples childTexture, or `gather`"},{"when":"a distortion from one coordinate function","use":"`defineShader` with `map:`"},{"when":"naming your shader in preset JSON for `createShader`","use":"`registerShader`, or the components option"},{"when":"listing the custom shaders an app has registered","use":"`getRegisteredShaders`, `onShaderRegistered`"}]},"values":{"id":"values","title":"Props & values","group":"define","module":"std/values.ts","access":{"kind":"top-level"},"curated":true,"words":["values.p","values.Scalar"],"types":["values.PropRef","values.ScalarInput","values.ScalarSource","values.SignalSlotParams"],"intro":"How a definition points at its own props. `p('radius')` is a **prop ref**: hand it to any word\nthat takes a color, a position or a number and the word reads that prop live on the GPU, with\ndrivers and animation applied, and no rebuild when the value changes. A **Scalar** is a number\nbuilt from props and masks. `radialMask(…)` returns one, so does `oscillating(…)` from\n`motion`, and `.times(p('intensity'))` scales one by a prop. Words that take a `ScalarInput`\naccept either a prop ref or a Scalar.\n\nProp refs and Scalars are data, not GPU values. A word turns them into GPU values when the\nshader is built. In a hand-written builder you do that yourself with `uniformOf` and\n`resolveScalar` from the invocation words.","example":"import {defineShader, p, tintToward, radialMask, crosses, isZero, transformColor, transformPosition} from 'shaders/std'\n\n// A vignette: tint the layer inside toward a color, strongest past a radius from a point.\nexport const Vignette = defineShader({\n  name: 'Vignette',\n  props: {\n    color: {default: '#000000', transform: transformColor},\n    center: {default: {x: 0.5, y: 0.5}, transform: transformPosition},\n    radius: {default: 0.4},\n    falloff: {default: 0.6},\n    intensity: {default: 1, recompile: crosses(0)},\n  },\n  effect: tintToward(p('color'), {\n    amount: radialMask({center: p('center'), radius: p('radius'), falloff: p('falloff')}).times(p('intensity')),\n  }),\n  identityWhen: isZero('intensity'),\n  missingChildMessage: 'Nest a layer inside Vignette to darken its edges.',\n})","reach":[{"when":"a word should read one of your props","use":"`p`"},{"when":"a strength that fades with distance from a point, scaled by a prop","use":"`radialMask` chained with .times(p('intensity')) on the `Scalar`"},{"when":"a value that breathes or pulses on its own","use":"`oscillating` or `pulsing` from motion, as a `Scalar`"},{"when":"a prop's value inside a hand-written builder","use":"`uniformOf`"}]},"slots":{"id":"slots","title":"Identity & recompile rules","group":"define","module":"std/slots.ts","access":{"kind":"top-level"},"curated":true,"words":["slots.crosses","slots.isZero","slots.isValue","slots.allOf","slots.identityWhenever","slots.recompileWhen"],"types":[],"intro":"Two small kinds of rule that make a filter cheap. An **identity rule** sits on a filter\ndefinition's `identityWhen:` and says when the filter does nothing: while it holds, the layer\ninside passes through untouched and the effect costs no GPU time. A **recompile rule** sits\non a prop's `recompile:` and says which changes rebuild the shader rather than just update a\nvalue.\n\nThey pair up. The skip is decided when the shader is built, so a prop the identity rule reads\nneeds `recompile: crosses(value)` at the same value. Cross it and the shader rebuilds with\nthe filter switched on or off. A prop that is mapped per pixel by a driver disables the skip\non its own, since its value then varies across the canvas.","example":"import {defineShader, p, crosses, isValue, effects} from 'shaders/std'\n\n// Saturation: free while intensity sits at 1, rebuilt only when it leaves or returns.\nexport const Saturation = defineShader({\n  name: 'Saturation',\n  props: {\n    intensity: {\n      default: 1,\n      recompile: crosses(1),\n      ui: {type: 'range', min: 0, max: 3, step: 0.1, label: 'Intensity'},\n    },\n  },\n  effect: effects.color.saturate(p('intensity')),\n  identityWhen: isValue('intensity', 1),\n  missingChildMessage: 'Nest a layer inside Saturation to adjust its color.',\n})","reach":[{"when":"skip a filter while a prop is 0","use":"`isZero`, with `crosses` on the prop"},{"when":"skip while a prop sits at its neutral value (1 for a gain)","use":"`isValue`, with `crosses` on the prop"},{"when":"skip only when several props are all at rest","use":"`allOf`"},{"when":"a no-op condition the rules cannot express","use":"`identityWhenever`"},{"when":"a rebuild threshold that is not a single value","use":"`recompileWhen`"}]},"invoke":{"id":"invoke","title":"Context & invocation","group":"define","module":"std/invoke.ts","access":{"kind":"top-level"},"curated":true,"words":["invoke.ctx","invoke.ctx.uv","invoke.ctx.time","invoke.ctx.aspect","invoke.ctx.pointer","invoke.ctx.viewportSize","invoke.ctx.logicalViewportSize","invoke.paintFrame","invoke.uniformOf","invoke.resolveScalar","invoke.resolveArg","invoke.resolveArgIn","invoke.pointwiseOp"],"types":["invoke.ArgSpec","invoke.CtxToken","invoke.SlotParams"],"intro":"The **context** is what every pixel knows about its surroundings: its coordinate (`uv`, 0–1\nacross the canvas with y down), the clock (`time`, in seconds), the canvas shape (`aspect`,\n`viewportSize`) and the mouse (`pointer`). `ctx` names these as tokens you can pass to a word\nor bind in a `wgsl` body's `inputs`.\n\nThe rest of this page is for a **hand-written builder**: a `paint:` or `effect:` given as a\nfunction of the params instead of a composition or a `wgsl` body. Inside one, `paintFrame`\ngives the coordinate and frame size to draw in, `uniformOf` reads a prop as a GPU value,\n`resolveScalar` and `resolveArg` turn anything a word accepts (a prop ref, a `Scalar`, a\ncontext token, a number) into a GPU value, and `pointwiseOp` wraps a compiled GPU function\nas a new color word. Combine the results with the `math` words and return a `vec4` color.","example":"import {defineShader, p, paintFrame, uniformOf, animatedTime, math, transformColor, transformPosition} from 'shaders/std'\n\nconst {sub, mul, add, vec2, vec4, length, smoothstep, mix, sin} = math\n\n// A spotlight: a soft disc around a draggable point that breathes on the layer's own clock.\nexport const Spotlight = defineShader({\n  name: 'Spotlight',\n  animatedTime: {speed: 'speed'},\n  props: {\n    colorA: {default: '#0b1026', transform: transformColor},\n    colorB: {default: '#ffd27a', transform: transformColor},\n    center: {default: {x: 0.5, y: 0.5}, transform: transformPosition},\n    radius: {default: 0.4},\n    speed: {default: 1},\n  },\n  paint: (params) => {\n    const {uv} = paintFrame(params)\n    const center = uniformOf(p('center'), params)\n    const colorA = uniformOf(p('colorA'), params)\n    const colorB = uniformOf(p('colorB'), params)\n    // A position prop's y is stored flipped; undo it to compare with uv.\n    const toCenter = sub(uv, vec2(center.member('x'), sub(1, center.member('y'))))\n    const d = length(mul(toCenter, vec2(params.ctx.aspect, 1)))\n    const breath = add(1, mul(0.1, sin(animatedTime(params))))\n    const t = sub(1, smoothstep(0, mul(uniformOf(p('radius'), params), breath), d))\n    return vec4(mix(colorA.member('rgb'), colorB.member('rgb'), t), 1)\n  },\n})","reach":[{"when":"the mouse, the clock or the canvas size as an input to a word","use":"`ctx`"},{"when":"the coordinate to draw at in a hand-written paint","use":"`paintFrame`"},{"when":"a prop's value inside a hand-written builder","use":"`uniformOf`"},{"when":"a `Scalar` (a mask times a prop) inside a builder","use":"`resolveScalar`"},{"when":"any value a word accepts, inside a filter builder","use":"`resolveArg`"},{"when":"the same inside a warp's map factory","use":"`resolveArgIn`"},{"when":"a new color word from a compiled GPU function","use":"`pointwiseOp`"}]},"lists":{"id":"lists","title":"List props","group":"define","module":"std/lists.ts","access":{"kind":"top-level"},"curated":true,"words":["lists.listOf","lists.accumulate"],"types":["lists.ListItemAt","lists.ListRef"],"intro":"A **list prop** holds any number of items of the same shape: lights, points, anchors. Declare\nit with `listPropConfig`: each item is a small record of typed fields (`position`, `color`,\n`number`, `boolean`) and the list has a `maxItems` cap. The editor renders it as an add and\nremove list, every position field becomes a draggable handle, and any position can follow the\nmouse. Adding or removing an item at runtime never rebuilds the shader.\n\nReading one takes a hand-written builder. `listOf(params, 'lights')` gives the live item count\nand each item's fields by name, and `accumulate` adds a per-item value over all of them in a\nreal GPU loop. Anything the loop body reads that was computed outside it goes in `deps`, so\nit is emitted once before the loop.","example":"import {defineShader, p, listPropConfig, paintFrame, uniformOf, listOf, accumulate, math, transformColor} from 'shaders/std'\n\nconst {sub, mul, add, div, vec2, vec4, length, clamp, splat3, local} = math\n\n// Point lights: each item in the list adds its colored glow over a background.\nexport const PointLights = defineShader({\n  name: 'PointLights',\n  props: {\n    background: {default: '#0b1026', transform: transformColor},\n    lights: listPropConfig({\n      maxItems: 6,\n      minItems: 1,\n      itemLabel: 'Light',\n      item: {\n        position: {kind: 'position', default: {x: 0.5, y: 0.5}, label: 'Position'},\n        color: {kind: 'color', default: '#ffb347', label: 'Color'},\n        intensity: {kind: 'number', default: 1, label: 'Intensity', min: 0, max: 4, step: 0.01},\n      },\n    }, {\n      default: [\n        {position: {x: 0.3, y: 0.3}, color: '#ffb347', intensity: 1},\n        {position: {x: 0.7, y: 0.7}, color: '#7a5cff', intensity: 1},\n      ],\n      description: 'The lights, each with its own position, color and brightness',\n      label: 'Lights',\n      group: 'Lights',\n    }),\n  },\n  paint: (params) => {\n    const {uv} = paintFrame(params)\n    const lights = listOf(params, 'lights')\n    const lit = accumulate(lights, (i) => {\n      const light = lights.at(i)\n      const pos = light.position('position')\n      // A position field's y is stored flipped; undo it to compare with uv.\n      const toLight = sub(uv, vec2(pos.member('x'), sub(1, pos.member('y'))))\n      const d = local(length(mul(toLight, vec2(params.ctx.aspect, 1))), 'lightDist')\n      return mul(light.color('color').member('rgb'), div(mul(light.number('intensity'), 0.05), add(0.01, mul(d, d))))\n    }, {zero: 'vec3f', deps: [uv], hint: 'lightSum'})\n    const base = uniformOf(p('background'), params).member('rgb')\n    return vec4(clamp(add(base, lit), splat3(0), splat3(1)), 1)\n  },\n})","reach":[{"when":"any number of lights, points or anchors as one prop","use":"listPropConfig to declare it, `listOf` to read it"},{"when":"adding up light, weight or distance from every item","use":"`accumulate`"},{"when":"one item's position, color or number at a loop index","use":"`listOf` then at(i)"}]},"paint":{"id":"paint","title":"Fields, palettes & ramps","group":"paint","module":"std/paint/fields.ts","access":{"kind":"namespace","name":"paint"},"curated":true,"words":["paint.rampOver","paint.pair","paint.stops","paint.standardPalette","paint.dist","paint.noiseField","paint.warped","paint.scaledVolume","paint.tone","paint.threshold","paint.rings","paint.tiles","paint.share","paint.flowField","paint.pulse","paint.shimmered","paint.wrapRamp","paint.foldRamp","paint.warpedPoint","paint.warpStep","paint.scatterField","paint.scatteredAnchors","paint.quadBlend","paint.colorLadder3","paint.hueWheel","paint.dist.conic","paint.dist.diamond","paint.dist.linear","paint.dist.radial","paint.dist.spiral"],"types":["paint.Field","paint.FieldScalar","paint.NoiseBasis","paint.Paint","paint.Palette"],"intro":"The words every gradient and most textures are drawn with. A **field** is a number per pixel\n(`Field`): the distance from a point, a noise value, a flow pattern. A **palette** turns a\nnumber into a color (`Palette`): two colors, or a multi-stop ramp, mixed in the color space\nyour definition declares. `rampOver` joins the two and is the single most common line in a\ngenerator.\n\nEverything else here shapes the field before it meets the palette. `warped` bends the\ncoordinate so smooth noise turns liquid. `scaledVolume` gives noise a zoomable, aspect-correct\ncoordinate. `tone` and `threshold` reshape the value. `rings` and `tiles` repeat it. `share`\nlets two words read one result. Fields nest, so read a recipe from the inside out: the\ninnermost word is sampled first and each wrapper changes what the next one sees.\n\nThe result is a paint: put it in the definition's `paint:` field.","example":"import {defineShader, p, paint, transformColor, transformColorSpace, colorSpaceOptions, colorStopsPropConfig} from 'shaders/std'\n\nconst {rampOver, tone, scaledVolume, warped, noiseField, stops} = paint\n\n// Liquid plasma: warped noise on a zoomable slab, tone-shaped, read through the stop ramp.\nexport const Plasma = defineShader({\n  name: 'Plasma',\n  animatedTime: {speed: 'speed'},\n  props: {\n    colorA: {default: '#7018be', transform: transformColor},\n    colorB: {default: '#000000', transform: transformColor},\n    stops: colorStopsPropConfig(),\n    colorSpace: {default: 'oklch', transform: transformColorSpace, compileTime: true, ui: {type: 'select', options: colorSpaceOptions}},\n    scale: {default: 2},\n    warp: {default: 0.4},\n    intensity: {default: 1.5},\n    speed: {default: 1},\n  },\n  paint: rampOver(\n    tone(\n      scaledVolume(warped(noiseField('mx3'), {amount: p('warp'), amountScale: 4, timeScale: 0.125}), {scale: p('scale')}),\n      {glow: p('intensity'), contrast: 1, balance: 50, invert: true},\n    ),\n    stops(p('colorSpace')),\n  ),\n})","reach":[{"when":"a gradient of any shape, in a chosen color space","use":"`rampOver` with a `dist` field and `pair` or `stops`"},{"when":"the field should flow like liquid","use":"`warped` around a `noiseField`, inside `scaledVolume`"},{"when":"noise you can zoom evenly at any canvas size","use":"`scaledVolume`"},{"when":"the value is too flat or too hot for the palette","use":"`tone`"},{"when":"a signed pattern should become soft color bands","use":"`threshold` over `flowField`"},{"when":"concentric or repeated bands of one field","use":"`rings`, `tiles`"},{"when":"two words need the same field without computing it twice","use":"`share`"},{"when":"a slow brightness breathing along a pattern","use":"`pulse` inside `shimmered`"},{"when":"the palette value runs past 1 and must not seam","use":"`wrapRamp`, `foldRamp`"},{"when":"a mesh-gradient look, colors scattered across the canvas","use":"`scatterField` with `scatteredAnchors`"},{"when":"three or four colors blended by two values","use":"`colorLadder3`, `quadBlend`"},{"when":"a rainbow from a hue number","use":"`hueWheel`"}]},"gradients":{"id":"gradients","title":"Gradients","group":"paint","module":"std/paint/gradients.ts","access":{"kind":"namespace","name":"gradients"},"curated":true,"words":["gradients.solidColor","gradients.beam","gradients.beamPreconvertedFields","gradients.colorWheel","gradients.pointCloudGradient"],"types":["gradients.Paint"],"intro":"Finished gradient paints: a flat fill, a color wheel that cycles over time, a cloud of five\ncolored points, and a beam of light between two points. Each word binds your props and\nreturns a paint, so a definition is the props plus one line. They are complete looks rather\nthan building blocks. For a gradient of your own shape, compose a `dist` field with `rampOver`\nfrom the fields words instead.\n\nColors mix in the color space you declare on a `compileTime` prop, and positions are props\nmade with `transformPosition`. The result goes in the definition's `paint:` field.","example":"import {defineShader, p, gradients, transformColor, transformPosition, transformColorSpace, colorSpaceOptions} from 'shaders/std'\n\n// A tapered beam: wide and soft at the start, tight at the end, red core fading to blue.\nexport const Beam = defineShader({\n  name: 'Beam',\n  extraFields: gradients.beamPreconvertedFields,\n  props: {\n    startPosition: {default: {x: 0.2, y: 0.5}, transform: transformPosition},\n    endPosition: {default: {x: 0.8, y: 0.5}, transform: transformPosition},\n    startThickness: {default: 0.4},\n    endThickness: {default: 0.1},\n    startSoftness: {default: 1},\n    endSoftness: {default: 0.3},\n    insideColor: {default: '#ff3b30', transform: transformColor},\n    outsideColor: {default: '#1e40ff', transform: transformColor},\n    colorSpace: {default: 'oklab', transform: transformColorSpace, compileTime: true, ui: {type: 'select', options: colorSpaceOptions}},\n  },\n  paint: gradients.beam({\n    from: p('startPosition'),\n    to: p('endPosition'),\n    thickness: {start: p('startThickness'), end: p('endThickness')},\n    softness: {start: p('startSoftness'), end: p('endSoftness')},\n    colors: {inside: p('insideColor'), outside: p('outsideColor')},\n    space: p('colorSpace'),\n  }),\n})","reach":[{"when":"a single color, possibly translucent","use":"`solidColor`"},{"when":"a rainbow or three-color cycle that drifts across the canvas","use":"`colorWheel`"},{"when":"five draggable color points blended by nearness","use":"`pointCloudGradient`"},{"when":"a soft beam of light from one point to another","use":"`beam` with `beamPreconvertedFields`"}]},"patterns":{"id":"patterns","title":"Patterns & tiling","group":"paint","module":"std/paint/patterns.ts","access":{"kind":"namespace","name":"patterns"},"curated":true,"words":["patterns.strokeOver","patterns.withAlpha","patterns.checkerCells","patterns.stripeBands","patterns.zigzagBands","patterns.ringWaves","patterns.fallingStreaks","patterns.dotLattice","patterns.cellFrame","patterns.gridLines","patterns.hexLines","patterns.triangleLines","patterns.brickCourses","patterns.truchetArcs","patterns.weaveThreads","patterns.isoCubeFaces","patterns.vary","patterns.sampleMapsAtCellCentres","patterns.times","patterns.mixOf","patterns.alphaOf","patterns.rgbOf","patterns.opaque","patterns.dotScreen","patterns.cmykPress","patterns.inkPlate","patterns.dotScreenMask","patterns.inkTransmission","patterns.chosenBy","patterns.pixelGrid","patterns.quantise","patterns.ditherInks","patterns.scatterPoint","patterns.scatterPoints"],"types":["patterns.CellFrameConvention","patterns.GatherStage","patterns.InkPlateSpec","patterns.PaintSlot","patterns.PatternPaint","patterns.PixelGrid","patterns.ScatterOptions","patterns.ScatteredPoint"],"intro":"Repeating patterns you can color and animate: checkers, stripes, zigzags, rings, falling\nstreaks, and tiled lattices of squares, hexagons, triangles, bricks, truchet arcs, woven\nthreads and isometric cubes, plus the print-screen filters (halftone dots, CMYK plates and\ndithering). Most pattern words return a **mask**: a number per pixel that is 1 on the pattern\nand 0 off it. The lattice words also return a per-cell **shade**, a brightness factor around 1\nthat `vary` multiplies into a fill so every tile reads slightly different.\n\nThe lattice words share one coordinate system: `cellFrame` sets how many cells fit down the\ncanvas height and how far the lattice is rotated, and `gridLines`, `hexLines`,\n`triangleLines`, `truchetArcs`, `weaveThreads` and `isoCubeFaces` draw inside it. The\nfree-standing words (`checkerCells`, `stripeBands`, `ringWaves`, `dotLattice`,\n`brickCourses`) bring their own. Color a mask with `strokeOver` (fill where 0, stroke where 1,\nin a chosen color space) or hang it under one color as alpha with `withAlpha`; the result goes\nin `paint:`. The halftone and dither words are filters over the layer inside them and go in\n`effect:`.","example":"import {defineShader, p, patterns, transformColor, transformColorSpace, transformAngle, colorSpaceOptions} from 'shaders/std'\n\nconst {cellFrame, hexLines, strokeOver, vary} = patterns\n\n// A honeycomb: hexagon lines in a rotatable lattice, each cell's fill lightened or darkened at random.\nconst field = hexLines({\n  frame: cellFrame({cells: p('cells'), rotation: p('rotation'), convention: 'clockwise'}),\n  thickness: p('thickness'),\n  softness: p('softness'),\n  variation: p('variation'),\n})\n\nexport const Honeycomb = defineShader({\n  name: 'Honeycomb',\n  props: {\n    fill: {default: '#101828', transform: transformColor},\n    line: {default: '#f5d90a', transform: transformColor},\n    colorSpace: {default: 'linear', transform: transformColorSpace, compileTime: true, ui: {type: 'select', options: colorSpaceOptions}},\n    cells: {default: 8},\n    thickness: {default: 1},\n    rotation: {default: 0, transform: transformAngle},\n    softness: {default: 0},\n    variation: {default: 0.3},\n  },\n  paint: strokeOver({\n    fill: vary(p('fill'), field.shade),\n    stroke: p('line'),\n    mask: field.lines,\n    space: p('colorSpace'),\n  }),\n})","reach":[{"when":"a two-color checkerboard","use":"`checkerCells` colored with `strokeOver`"},{"when":"stripes or chevrons that scroll","use":"`stripeBands`, `zigzagBands`"},{"when":"rings rippling out from a point","use":"`ringWaves`"},{"when":"rain or streaks falling across the canvas","use":"`fallingStreaks` with `withAlpha`"},{"when":"a field of dots, optionally staggered or twinkling","use":"`dotLattice` under `withAlpha`"},{"when":"grid lines, honeycombs or triangle lattices","use":"`cellFrame` with `gridLines`, `hexLines`, `triangleLines`"},{"when":"every tile a slightly different brightness","use":"`vary` with a lattice word's shade output"},{"when":"a brick wall","use":"`brickCourses`"},{"when":"a maze of joined arcs","use":"`truchetArcs`"},{"when":"a woven textile","use":"`weaveThreads`"},{"when":"tumbling 3D cubes","use":"`isoCubeFaces` with `times`, `mixOf`, `opaque`"},{"when":"a mapped size or thickness that should change per whole cell","use":"`sampleMapsAtCellCentres` on the definition's mapSampleUVs field"},{"when":"a halftone dot screen over the layer inside","use":"`dotScreen`"},{"when":"a four-color print with plate angles and misregistration","use":"`cmykPress` with `inkPlate`"},{"when":"your own ink press inside a gather build","use":"`dotScreenMask`, `inkTransmission`"},{"when":"one select prop that swaps whole effect recipes","use":"`chosenBy`"},{"when":"a retro dither","use":"`pixelGrid`, `quantise`, `ditherInks`"},{"when":"a color's alpha or RGB on its own","use":"`alphaOf`, `rgbOf`"}]},"figures":{"id":"figures","title":"Figures & strokes","group":"paint","module":"std/paint/figures.ts","access":{"kind":"namespace","name":"figures"},"curated":true,"words":["figures.strokedSegment","figures.sineStroke"],"types":["figures.Paint","figures.SineStrokeSlots","figures.StrokedSegmentSlots"],"intro":"Drawn lines: a straight segment and a traveling sine wave. Each word binds your props and\nreturns a paint that draws the stroke in one color prop with an antialiased edge and leaves\neverything else transparent, so it layers cleanly over what sits behind it.\n\nLengths are fractions of the canvas height, so a stroke keeps its weight when the canvas\nresizes. Endpoints and centers are position props made with `transformPosition`. The result\ngoes in the definition's `paint:` field.","example":"import {defineShader, p, figures, transformColor, transformPosition} from 'shaders/std'\n\n// A dashed line with round ends. The style select maps its labels to the numbers the word takes.\nexport const DashedLine = defineShader({\n  name: 'DashedLine',\n  props: {\n    color: {default: '#ffffff', transform: transformColor},\n    pointA: {default: {x: 0.2, y: 0.5}, transform: transformPosition},\n    pointB: {default: {x: 0.8, y: 0.5}, transform: transformPosition},\n    thickness: {default: 0.01, ui: {type: 'range', min: 0, max: 0.25, step: 0.001}},\n    style: {\n      default: 'dashed',\n      transform: (value: string) => ({solid: 0, dashed: 1, dotted: 2} as Record<string, number>)[value] ?? 0,\n      ui: {type: 'select', options: [{label: 'Solid', value: 'solid'}, {label: 'Dashed', value: 'dashed'}, {label: 'Dotted', value: 'dotted'}]},\n    },\n    dashLength: {default: 0.05},\n    gapLength: {default: 0.025},\n  },\n  paint: figures.strokedSegment({\n    from: p('pointA'),\n    to: p('pointB'),\n    width: p('thickness'),\n    style: p('style'),\n    dashLength: p('dashLength'),\n    gapLength: p('gapLength'),\n    capStart: 1,\n    capEnd: 1,\n    color: p('color'),\n  }),\n})","reach":[{"when":"a line between two draggable points, solid, dashed or dotted","use":"`strokedSegment`"},{"when":"a wavy line that ripples along itself","use":"`sineStroke`"}]},"compose":{"id":"compose","title":"Layering & output","group":"paint","module":"std/paint/compose.ts","access":{"kind":"namespace","name":"compose"},"curated":true,"words":["compose.layers","compose.emissiveAlpha","compose.dithered","compose.layered"],"types":["compose.Layer"],"intro":"The words that finish a `paint:` function. `layers` stacks several colors into one, back to\nfront, each with an optional opacity and an optional factor it is seen through. `layered`\nbuilds N variations of one recipe from a data list and sums them, which is how curtains,\ncloud banks and echo trails get their depth cheaply.\n\nLight is emitted, so it should composite *over* what is behind the layer rather than replace\nit. `emissiveAlpha` turns an rgb light value into a color with alpha that does exactly that.\n`dithered` adds a trace of noise to the final color so long soft ramps show no banding. Use\nthem last: stack with `layers`, close with `emissiveAlpha`, then `dithered`.","example":"import {defineShader, p, light, compose, math, transformColor, transformPosition} from 'shaders/std'\n\nconst {radialFrame, withFrame, glowPoint} = light\nconst {layers, emissiveAlpha, dithered} = compose\n\n// Two soft glows, the second screened over the first, drawn as emitted light and dithered.\nexport const TwinGlow = defineShader({\n  name: 'TwinGlow',\n  animatedTime: {speed: 'speed'},\n  props: {\n    centerA: {default: {x: 0.35, y: 0.5}, transform: transformPosition},\n    centerB: {default: {x: 0.65, y: 0.5}, transform: transformPosition},\n    colorA: {default: '#ff7a18', transform: transformColor},\n    colorB: {default: '#3b82f6', transform: transformColor},\n    sharpness: {default: 6},\n    speed: {default: 0},\n  },\n  paint: (params) => {\n    const glowA = withFrame(radialFrame(p('centerA')), glowPoint({sharpness: p('sharpness')}), 'glowA')(params)\n    const glowB = withFrame(radialFrame(p('centerB')), glowPoint({sharpness: p('sharpness')}), 'glowB')(params)\n    const rgb = layers([\n      {paint: math.mul(params.uniforms.colorA.member('rgb'), glowA)},\n      {paint: math.mul(params.uniforms.colorB.member('rgb'), glowB), blend: 'screen'},\n    ])\n    return dithered(emissiveAlpha(rgb), params)\n  },\n})","reach":[{"when":"several glows or paints stacked into one image","use":"`layers`"},{"when":"the same recipe repeated with different offsets and weights","use":"`layered`"},{"when":"a glow that should composite over transparency","use":"`emissiveAlpha`"},{"when":"visible steps in a long soft gradient","use":"`dithered`"}]},"media":{"id":"media","title":"Media","group":"paint","module":"std/paint/media.ts","access":{"kind":"namespace","name":"media"},"curated":true,"words":["media.imageMedia","media.videoMedia","media.webcamMedia"],"types":["media.MediaSurfaceHalf"],"intro":"Pixels that come from an image, a video or the user's camera instead of from math. Each word\nloads its source, fits it into the layer the way CSS `object-fit` fits an image into a box,\nand returns the GPU half of a definition. Spread it into `defineShader({...})` next to your\nprops, with `role: 'media'` and `species: 'custom'`.\n\nEvery media word takes a `fit` prop (`'cover'`, `'contain'`, `'fill'` or `'scale-down'`,\nmarked `compileTime`) and `decode: 'srgb-linear-alpha-cut'`, which reads the source as sRGB\nand makes the letterbox around a contained image transparent. Set `naturalSizeKey` on the\ndefinition so layout can measure the media. Each word takes an `onError` callback so you can\nphrase failures in your own words.","example":"import {defineShader, p, media} from 'shaders/std'\n\n// An image from a URL, fitted like CSS object-fit. The fit prop is a plain string.\nexport const Photo = defineShader({\n  name: 'Photo',\n  role: 'media',\n  species: 'custom',\n  naturalSizeKey: {fromProp: 'url'},\n  props: {\n    url: {default: 'https://shaders.com/sample.jpg', ui: {type: 'image-upload', label: 'Image'}},\n    objectFit: {\n      default: 'cover',\n      compileTime: true,\n      ui: {type: 'select', options: [{label: 'Cover', value: 'cover'}, {label: 'Contain', value: 'contain'}, {label: 'Fill', value: 'fill'}, {label: 'Scale down', value: 'scale-down'}]},\n    },\n  },\n  ...media.imageMedia({\n    src: p('url'),\n    fit: p('objectFit'),\n    decode: 'srgb-linear-alpha-cut',\n    label: 'Photo',\n    onError: (error, url) => console.error(`Photo: could not load ${url}`, error),\n  }),\n})","reach":[{"when":"a photo or logo from a URL","use":"`imageMedia`"},{"when":"a looping video from a URL","use":"`videoMedia`"},{"when":"the user's camera, with a selfie mirror","use":"`webcamMedia`"}]},"noise":{"id":"noise","title":"Noise","group":"light","module":"std/paint/noise.ts","access":{"kind":"namespace","name":"noise"},"curated":true,"words":["noise.seededPlane","noise.evolving","noise.noiseTone","noise.unitized","noise.signedTone","noise.gainTone","noise.gaborGrains","noise.waveletBands","noise.curlSpeed","noise.erosionRidges","noise.scratchStreaks","noise.pixelGrid","noise.blueSpeckle","noise.scaledPlane","noise.linearPair","noise.fractalNoise","noise.worleyNoise","noise.transformWorleyMode","noise.transformWorleyDistance","noise.cellDistances","noise.cellFill","noise.cellBorders","noise.borderOverlay","noise.waves","noise.wavyLine","noise.organicWaves","noise.ribbons","noise.overRgb","noise.backToP3","noise.tonePow","noise.noiseRelief","noise.reliefBases","noise.clothCoords","noise.drapedFolds","noise.foldSet","noise.reliefBases.stone","noise.reliefBases.wool"],"types":["noise.DrapeOptions","noise.Fold","noise.ReliefBasis","noise.RgbStage","noise.Wave","noise.WaveTerm"],"intro":"Words for grainy, cloudy, cellular and streaked surfaces: the textures behind clouds, film\ngrain, stone, water and brushed metal. Read a noise recipe from the inside out, in four\nsteps. A **basis** is the raw pattern, one number per point (`gaborGrains`, `curlSpeed`,\n`blueSpeckle`, or a `noiseField` from paint). A **framing** decides where on the canvas the\nbasis is sampled and how big it is: `seededPlane` for almost everything, `pixelGrid` for\npatterns that live on pixels. A **tone** word turns the raw number into a 0–1 ramp position\nwith contrast and balance controls: `noiseTone` for a 0–1 basis, `signedTone` for a −1 to 1\none, or `unitized` to convert first. A **ramp** closes it: `rampOver` with `stops` or\n`linearPair` gives the field color.\n\nEach basis is either signed (−1 to 1) or unit (0 to 1); its comment says which. Signed\nbases go through `unitized` or `signedTone`, unit bases straight into `noiseTone`. Bases\nthat move read the layer's clock, so declare `animatedTime: {speed: 'speed'}` on the\ndefinition. `fractalNoise` and `worleyNoise` are complete paints that do all four steps\nfor you. The wave words (`waves`, `wavyLine`) are expressions rather than fields, for\nrecipes written as a `paint: (params) => …` function. `noiseRelief` is the one filter here:\nit embosses the layer inside instead of painting.","example":"import {defineShader, p, noise, paint, transformColor, transformColorSpace, colorSpaceOptions} from 'shaders/std'\n\nconst {seededPlane, gaborGrains, unitized, noiseTone} = noise\nconst {rampOver, stops} = paint\n\n// Brushed metal: oriented sine grains (a signed basis) framed on a zoomable plane,\n// converted to 0–1, toned with contrast and balance, and read through the color ramp.\nexport const BrushedMetal = defineShader({\n  name: 'BrushedMetal',\n  animatedTime: {speed: 'speed'},\n  props: {\n    colorA: {default: '#f4f4f5', transform: transformColor},\n    colorB: {default: '#27272a', transform: transformColor},\n    colorSpace: {default: 'oklab', transform: transformColorSpace, compileTime: true, ui: {type: 'select', options: colorSpaceOptions}},\n    scale: {default: 1.5},\n    frequency: {default: 8},\n    seed: {default: 0},\n    contrast: {default: 0},\n    balance: {default: 0},\n    speed: {default: 0.5},\n  },\n  paint: rampOver(\n    noiseTone(\n      seededPlane(unitized(gaborGrains({frequency: p('frequency')})), {scale: p('scale'), seed: p('seed')}),\n      {contrast: p('contrast'), balance: p('balance')},\n    ),\n    stops(p('colorSpace')),\n  ),\n})","reach":[{"when":"soft drifting clouds or smoke","use":"`seededPlane` around `evolving` around `noiseField`, then `noiseTone`"},{"when":"layered fractal detail with an angle","use":"`fractalNoise`"},{"when":"cells, scales or a cracked-earth look","use":"`worleyNoise`"},{"when":"Voronoi cells with a fill and border lines","use":"`cellDistances` with `cellFill`, `cellBorders` and `borderOverlay`"},{"when":"brushed metal or wood grain","use":"`gaborGrains` inside `unitized`"},{"when":"rippling water interference","use":"`waveletBands` inside `unitized`"},{"when":"film grain or dither","use":"`pixelGrid` around `blueSpeckle`"},{"when":"eroded terrain ridges","use":"`erosionRidges` inside `unitized`"},{"when":"swirling flow, eddies and streams","use":"`curlSpeed`"},{"when":"hairline scratches on a surface","use":"`scratchStreaks`"},{"when":"a basis is −1 to 1 and the ramp wants 0–1","use":"`unitized` or `signedTone`"},{"when":"the standard contrast and balance sliders","use":"`noiseTone`"},{"when":"a contrast slider where 1 means unchanged","use":"`gainTone`"},{"when":"two colors with no color-space prop","use":"`linearPair`"},{"when":"an organic wobble on an edge, radius or fill","use":"`waves`, or `organicWaves` for a ready schedule"},{"when":"a wavy line, horizon or curtain path","use":"`wavyLine`"},{"when":"flowing gradient ribbons between two points","use":"`ribbons` closed by `overRgb` with `backToP3` and `tonePow`"},{"when":"embossing the layer inside as stone or cloth","use":"`noiseRelief` with `reliefBases`"}]},"light":{"id":"light","title":"Light","group":"light","module":"std/paint/light.ts","access":{"kind":"namespace","name":"light"},"curated":true,"words":["light.glowAt","light.glowSpot","light.emitterFalloff","light.radialFrame","light.seamlessRadialFrame","light.beamFrame","light.withFrame","light.modulate","light.glowPoint","light.featherMask","light.rayLobes","light.softRayLobes","light.raySpikes","light.ringBand","light.noiseRays","light.rayBands","light.shine","light.domeNormal","light.additive","light.heatRamp","light.vignetteMask","light.screenGlow","light.coverageMix","light.coverageOver","light.slowDrift","light.beamBloom","light.beamStreaks","light.streakBand","light.lightBeam","light.segmentProject","light.flareFrame","light.lensGhost","light.flareHalo","light.flareStarburst","light.flareStreak","light.flareGlare","light.flareCore","light.flareComposite","light.dithered","light.starGlint"],"types":["light.HeatRamp","light.LightPart","light.LightValue"],"intro":"Words for drawing light: glows, rays, beams, flares and halos, plus the frames they are drawn\nin. A **frame** is the coordinate system a light evaluates in. `radialFrame` gives each pixel\nits distance and angle from a center; `beamFrame` gives its distance along and across a beam.\n**Parts** such as `glowPoint`, `rayLobes` and `featherMask` shape a light over a frame and\nreturn a *field*, a 0–1 number per pixel. `withFrame` evaluates a part in a frame,\n`modulate` multiplies parts (a mask over a pattern), and `additive` sums finished lights.\n\nA light field becomes color at the end of the recipe. `coverageMix` and `coverageOver` color\na field over a background as a finished `paint:`. `heatRamp` turns a heat field into\noverexposed color, and `screenGlow` shines it onto the layer inside as an `effect:`. Emitted\nlight drawn over transparency closes with `emissiveAlpha` from `compose`.","example":"import {defineShader, p, light, compose, math, transformColor, transformPosition} from 'shaders/std'\n\nconst {radialFrame, withFrame, modulate, glowPoint, softRayLobes, featherMask} = light\n\n// A sun: soft rays spinning around a draggable center, feathered at a radius, drawn as emitted light.\nexport const Sun = defineShader({\n  name: 'Sun',\n  animatedTime: {speed: 'speed'},\n  props: {\n    center: {default: {x: 0.5, y: 0.5}, transform: transformPosition},\n    color: {default: '#ffd27a', transform: transformColor},\n    radius: {default: 0.5},\n    rays: {default: 12},\n    softness: {default: 0.6},\n    speed: {default: 1},\n  },\n  paint: (params) => {\n    const coverage = withFrame(\n      radialFrame(p('center')),\n      modulate(softRayLobes({count: p('rays'), softness: p('softness'), spin: 0.2}), glowPoint({sharpness: 6}), featherMask({radius: p('radius'), feather: 0.4})),\n      'sun',\n    )(params)\n    const rgb = math.mul(params.uniforms.color.member('rgb'), coverage)\n    return compose.emissiveAlpha(rgb)\n  },\n})","reach":[{"when":"a soft round glow at a position","use":"`glowAt`"},{"when":"a glow, rays or a halo around a center prop","use":"`radialFrame` + `withFrame` with `glowPoint`, `rayLobes`, `ringBand`"},{"when":"a light that fades at a radius","use":"`featherMask`"},{"when":"light spilling from an emitter with a real falloff","use":"`emitterFalloff`"},{"when":"volumetric rays or god rays","use":"`noiseRays` in a `seamlessRadialFrame`"},{"when":"coloring a light field over a background","use":"`coverageMix` or `coverageOver`"},{"when":"a beam anchored at a point aimed at the canvas","use":"`beamFrame` with `beamBloom`, `beamStreaks`"},{"when":"a beam between two points, tapered end to end","use":"`segmentProject` + `lightBeam`"},{"when":"a camera lens flare","use":"`flareFrame` + the `flare*` parts, closed with `flareComposite`"},{"when":"a shiny surface from a normal field","use":"`shine`"},{"when":"turning a heat field into overexposed color","use":"`heatRamp`"},{"when":"darkening the corners","use":"`vignetteMask`"},{"when":"a glow that behaves like exposure over a child","use":"`screenGlow`"}]},"materials":{"id":"materials","title":"Materials & surfaces","group":"light","module":"std/paint/materials.ts","access":{"kind":"namespace","name":"materials"},"curated":true,"words":["materials.shapedSurface","materials.surfaceField","materials.geometricNormal","materials.marchedNormal","materials.nudgeNormal","materials.tiltNormal","materials.tiltAlong","materials.fdSlope","materials.perlinSlope","materials.fieldSlope","materials.surfacePattern","materials.keyLightAt","materials.keyLightXY","materials.lightVec3","materials.placementPoint","materials.pointLightFrom","materials.inverseSquare","materials.lambert","materials.sharpGlint","materials.dualLobeGlint","materials.anisoSpecular","materials.wardAlphas","materials.viewRay","materials.grazingOf","materials.grazingFlat","materials.fresnelBoost","materials.schlickFresnel","materials.studioSoftboxes","materials.smearAlong","materials.opticalThickness","materials.beerLambert","materials.cosineRainbow","materials.tintRamp","materials.neutralTone","materials.exposureTone","materials.silhouette","materials.insideShape","materials.guarded","materials.surfaceNoise","materials.surfaceNoiseAt","materials.volumeNoiseAt","materials.cellNoiseAt","materials.valueNoise","materials.hashNoise","materials.interleavedNoise","materials.grainNoise","materials.flowWarp","materials.sensorGrain","materials.pointStars","materials.twinkle","materials.shellRefract","materials.rotationSensor","materials.nearestEdge","materials.continuedField","materials.fdCurvature","materials.sheenLobe"],"types":["materials.ShapedSurfaceEffect","materials.ShapedSurfaceSpec","materials.SurfaceField","materials.SurfaceFrame"],"intro":"Words that shade a surface so a shape reads as glass, metal, plastic or stone. A material is\nthree things: a **normal** (which way the surface faces at this pixel), a **light** (where it\ncomes from) and a **response** (how the surface answers: diffuse, glint, mirror, tint).\n`shapedSurface` turns a shape prop into a surface you can shade. It hands your `surface`\nfunction a frame with the shape's distance field, and `surfaceField` binds what you read from\nit: the signed distance, the pixel size, the neighbour taps.\n\nFrom there a material is a recipe read top to bottom. A normal from `geometricNormal`, bent\nby `nudgeNormal` or `tiltNormal` for relief. A light from `keyLightAt` (an angle) or\n`pointLightFrom` (a position). A response from `lambert`, `sharpGlint`, `dualLobeGlint`,\n`schlickFresnel`, `beerLambert`. A finish from `neutralTone` and `silhouette`, closed with\n`guarded` around `insideShape` so pixels outside the shape pay nothing. The same words light\nanything with a slope, not only shapes: `fdSlope` of a noise field feeds `nudgeNormal`, and\n`keyLightAt` with `lambert` shades it.\n\nTwo conventions to hold. The viewer looks along +z, so a normal that faces the viewer has\nnegative z and a key light on the viewer's side has a negative elevation (−0.6 raking to\n−0.9 frontal). And a trailing `hint` argument only names a value in the compiled shader for\ndebugging. Leave it out.","example":"import {defineShader, p, uniformOf, paint, frames, materials, compose, math, transformColor, transformColorSpace, colorSpaceOptions} from 'shaders/std'\n\nconst {add, mul, dot, vec3, vec4} = math\nconst {noiseField, warped, scaledVolume, tone, pair} = paint\nconst {surfaceOf, direction} = frames\nconst {fdSlope, nudgeNormal, keyLightAt, lambert, dualLobeGlint, neutralTone} = materials\n\n// One height field drives everything: warped noise on a zoomable slab.\nconst height = scaledVolume(\n  warped(noiseField('mx3'), {amount: p('warp'), amountScale: 4, timeScale: 0.12}),\n  {scale: p('scale')},\n)\n\n// Silk: the field's slope becomes a normal, a key light shades it, a two-lobe sheen catches the folds.\nexport const SilkTide = defineShader({\n  name: 'SilkTide',\n  animatedTime: {speed: 'speed'},\n  props: {\n    colorA: {default: '#0b1026', transform: transformColor},\n    colorB: {default: '#ff7e5f', transform: transformColor},\n    colorSpace: {default: 'oklch', transform: transformColorSpace, compileTime: true, ui: {type: 'select', options: colorSpaceOptions}},\n    highlight: {default: '#fff3dc', transform: transformColor},\n    scale: {default: 1.8},\n    warp: {default: 0.55},\n    lightAngle: {default: 35},\n    sheen: {default: 1},\n    speed: {default: 1},\n  },\n  paint: (params) => {\n    const {uv} = surfaceOf(params)\n    const u = (name: string) => uniformOf(p(name), params)\n\n    // 1 · the field and its slope: three taps of the noise, bound once\n    const relief = fdSlope((at) => height(params, at), uv, 0.0035, 'tide')\n\n    // 2 · color: tone-shape the field and read it through the two-color palette\n    const shaped = tone(() => relief.value, {glow: 5, contrast: 1.3, balance: 50, invert: true})(params, uv)\n    const base = pair(p('colorA'), p('colorB'), p('colorSpace'))(shaped, params)\n\n    // 3 · light: slope → normal → key light → a wrapped diffuse and a sheen\n    const tilt = mul(u('sheen'), -0.08)\n    const n = nudgeNormal(vec3(0, 0, -1), mul(relief.dx, tilt), mul(relief.dy, tilt), 'tideN')\n    const key = keyLightAt(direction(u('lightAngle'), 'tideDir'), -0.7, 'tideKey')\n    const diffuse = add(0.55, mul(0.45, lambert(n, key.member('L'), {wrap: 0.5})))\n    const spec = dualLobeGlint(dot(n, key.member('H')), {core: [140, 1], halo: [22, 0.15], gain: u('sheen')})\n\n    const rgb = neutralTone(add(mul(base.member('rgb'), diffuse), mul(u('highlight').member('rgb'), spec)))\n    return compose.dithered(vec4(rgb, base.member('a')), params)\n  },\n})","reach":[{"when":"a shape prop should become a shaded surface","use":"`shapedSurface`, then `surfaceField` as the first line"},{"when":"the surface normal of whatever shape is active","use":"`geometricNormal`"},{"when":"relief from a height field or noise","use":"`fdSlope` or `perlinSlope` into `nudgeNormal`"},{"when":"brushed or woven relief along a direction","use":"`tiltNormal` with `grainNoise`"},{"when":"a light from an angle prop","use":"`keyLightAt`"},{"when":"a light from a position prop","use":"`placementPoint` into `pointLightFrom`, faded by `inverseSquare`"},{"when":"soft diffuse shading","use":"`lambert`"},{"when":"a highlight that follows a sharpness slider","use":"`sharpGlint`"},{"when":"a highlight with a hot core and a wide halo","use":"`dualLobeGlint`"},{"when":"a highlight stretched along a grain","use":"`anisoSpecular` with `wardAlphas`"},{"when":"brighter or more mirror-like rims","use":"`fresnelBoost`, `schlickFresnel` over `grazingOf` or `grazingFlat`"},{"when":"a studio reflection in a metal","use":"`viewRay`, `reflect`, `studioSoftboxes`, smeared by `smearAlong`"},{"when":"glass, ice or water that darkens with thickness","use":"`opticalThickness` into `beerLambert`"},{"when":"a rainbow sheen","use":"`cosineRainbow`"},{"when":"stars, glitter or dust","use":"`pointStars` with `twinkle`"},{"when":"a soft edge and nothing drawn outside the shape","use":"`silhouette` inside `guarded` on `insideShape`"},{"when":"bright light brought back into range","use":"`neutralTone` for a surface, `exposureTone` for emitted light"},{"when":"film grain or per-pixel jitter","use":"`sensorGrain`, `hashNoise`, `interleavedNoise`"},{"when":"a rim glow or contact shadow outside the shape","use":"`nearestEdge`, `continuedField`"}]},"volume":{"id":"volume","title":"Volumes","group":"light","module":"std/paint/volume.ts","access":{"kind":"namespace","name":"volume"},"curated":true,"words":["volume.interiorRay","volume.volumeMarch","volume.turbulentMedium","volume.parallaxPlane"],"types":["volume.InteriorRay","volume.VolumeMarchStep"],"intro":"Words for filling a shape with something you look through: smoke, gas, tinted glass, a\nstarfield behind a window. They work inside a shape-effect material, the `surface:` of\n`shapedSurface` from `materials`. An **interior ray** is the line of sight through the shape:\nwhere it enters, how far it travels inside, and where it is at any depth. A **medium** is a\ndensity at each point inside. A **march** walks the ray front to back, adding up what each\nsample glows and how much it dims what lies behind it.\n\nStart with `surfaceField` and `viewRay`, build the ray with `interiorRay`, then call\n`volumeMarch` and sample `turbulentMedium` at `ray.at(z)` inside its callback. `parallaxPlane`\nplaces a backdrop at a depth behind the gas. The march returns a color and a transmittance.\nLayer the color over anything behind it multiplied by the transmittance, take the alpha\nfrom `silhouette`, and wrap it all in `guarded` with `insideShape` so pixels outside the\nshape stay cheap.","example":"import {defineShader, animatedTime, materials, volume, math, transformColor, transformPosition} from 'shaders/std'\n\nconst {shapedSurface, surfaceField, viewRay, hashNoise, silhouette, guarded, insideShape} = materials\nconst {interiorRay, turbulentMedium, volumeMarch} = volume\nconst {float, local, mul, smoothstep, splat3, sub, vec4} = math\n\nconst STEPS = 6\n\n// Smoke sealed inside any shape: a six-step march through billowing density, glowing in the smoke color.\nexport const Smoke = defineShader({\n  name: 'Smoke',\n  role: 'shapeEffect',\n  species: 'custom',\n  animatedTime: {speed: 'speed'},\n  props: {\n    center: {default: {x: 0.5, y: 0.5}, transform: transformPosition},\n    scale: {default: 1},\n    rotation: {default: 0},\n    color: {default: '#ffb27a', transform: transformColor},\n    density: {default: 1},\n    gasScale: {default: 1},\n    edgeSoftness: {default: 0.01},\n    speed: {default: 1},\n    shape: {default: JSON.stringify({type: 'sphere3D', radius: 0.35}), ui: {type: 'shape', label: 'Shape'}},\n    shapeSdfUrl: {default: '', compileTime: true},\n    shapeType: {default: '', compileTime: true},\n  },\n  ...shapedSurface({\n    stencil: 'centre',\n    centreTap: 'fast',\n    surface: (frame, params) => {\n      const u = params.uniforms\n      const field = surfaceField(frame, params, {scale: u.scale})\n      const view = viewRay(params, field)\n      // The shape's own coordinates, centred, as the point the ray enters at.\n      const origin = {x: sub(frame.sdfUV!.member('x'), 0.5), y: sub(frame.sdfUV!.member('y'), 0.5)}\n      const ray = interiorRay(field, frame, {origin, view, shear: 0.5})\n      const drift = local(mul(animatedTime(params), 0.15), 'drift')\n      const jitter = mul(hashNoise(mul(origin.x, 311.7), mul(origin.y, 173.3)), 0.15 / STEPS)\n      const march = volumeMarch(\n        {steps: STEPS, entry: ray.entry, length: ray.length, jitter, emissionGain: float(4 / STEPS)},\n        ({i, z}) => {\n          const at = ray.at(z)\n          const gas = turbulentMedium({x: at.x, y: at.y, z}, {frequency: mul(u.gasScale, 3), drift, billow: float(0.4), hint: `${i}`})\n          const dens = local(smoothstep(0, 0.6, gas.density), `dens${i}`)\n          return {emit: mul(u.color.member('rgb'), dens), absorb: splat3(mul(dens, mul(u.density, 3 / STEPS)))}\n        },\n      )\n      const smoke = vec4(march.color, silhouette(field, u.edgeSoftness))\n      return guarded(insideShape(field.sdf, field.pxH), smoke, vec4(0, 0, 0, 0), 'smoke')\n    },\n  }),\n})","reach":[{"when":"smoke or gas sealed inside a shape","use":"`interiorRay` and `volumeMarch` over `turbulentMedium`"},{"when":"a backdrop or starfield seen through the shape","use":"`parallaxPlane` behind the march"},{"when":"a medium that tints what lies behind it","use":"a per-channel absorb in `volumeMarch`"},{"when":"cloud density with free dust and wisp detail","use":"`turbulentMedium`"}]},"radiance":{"id":"radiance","title":"Radiance","group":"light","module":"std/paint/radiance.ts","access":{"kind":"namespace","name":"radiance"},"curated":true,"words":["radiance.irradianceField","radiance.shadowVisibility","radiance.gatherIrradiance"],"types":["radiance.GatherSpec","radiance.IrradianceFieldSpec","radiance.ShadowSpec"],"intro":"Words for light a shape gives off or blocks. Colored point lights strike the shape's\nsilhouette, the lit edges spill light into their surroundings, and the shape's body casts\nshadows, so light stays out of a ring's hole and off the far side of a wall. They work inside\na shape-effect material, the `surface:` of `shapedSurface` from `materials`. The shape is a\n**signed distance**, a function from a point to its distance from the edge, and every word\nhere marches rays against it.\n\n`irradianceField` does the heavy work once: it gathers the spilled light into a texture in a\ncompute pass and only gathers again when a light or the shape changes, so a still scene\ncosts one texture read per pixel. Build its spec once, put its `compute` on your definition\nand read `sample(params)` in your material. `shadowVisibility` is for light you compute\nyourself at a point, such as the directly lit rim. `gatherIrradiance` is the same gather run\nlive per pixel, for when the light must change every frame. The light comes back as rgb.\nSend it through `exposureTone` and `emissiveAlpha` for a glow that composites over what is\nbehind it, then mix in the body by `silhouette`.","example":"import {defineShader, listPropConfig, materials, radiance, compose, math, transformColor, transformPosition} from 'shaders/std'\n\nconst {shapedSurface, surfaceField, silhouette, exposureTone} = materials\nconst {irradianceField} = radiance\nconst {emissiveAlpha} = compose\nconst {local, mix, splat3, vec4} = math\n\n// One spec for both halves: the compute gather and the fragment read.\nconst GATHER = {\n  rays: {burst: 128, motion: 64, refine: 32, refineFrames: 12},\n  steps: 32,\n  shadowSteps: 12,\n  resolution: 512,\n  lights: {prop: 'lights', position: 'position', color: 'color', intensity: 'intensity'},\n  reach: 'reach',\n  wrap: 'wrap',\n  lightRange: 'lightRange',\n  shadowSoftness: 'shadowSoftness',\n}\n\n// Colored lights spilling around a dark shape, gathered once and read per pixel.\nexport const LitEdge = defineShader({\n  name: 'LitEdge',\n  role: 'shapeEffect',\n  species: 'custom',\n  props: {\n    center: {default: {x: 0.5, y: 0.5}, transform: transformPosition},\n    scale: {default: 1},\n    rotation: {default: 0},\n    lights: listPropConfig({\n      maxItems: 6,\n      minItems: 1,\n      itemLabel: 'Light',\n      item: {\n        position: {kind: 'position', default: {x: 0.3, y: 0.3}, label: 'Position'},\n        color: {kind: 'color', default: '#ffb347', label: 'Color'},\n        intensity: {kind: 'number', default: 3, label: 'Intensity', min: 0, max: 8, step: 0.01},\n      },\n    }, {\n      default: [\n        {position: {x: 0.3, y: 0.3}, color: '#ffb347', intensity: 3},\n        {position: {x: 0.72, y: 0.7}, color: '#7a5cff', intensity: 2},\n      ],\n      description: 'The point lights striking the shape',\n      label: 'Lights',\n      group: 'Lights',\n    }),\n    lightRange: {default: 1.2},\n    reach: {default: 3},\n    wrap: {default: 0},\n    shadowSoftness: {default: 0.2},\n    bodyColor: {default: '#000000', transform: transformColor},\n    edgeSoftness: {default: 0.01},\n    shape: {default: JSON.stringify({type: 'circleSDF', radius: 0.35}), ui: {type: 'shape', label: 'Shape'}},\n    shapeSdfUrl: {default: '', compileTime: true},\n    shapeType: {default: '', compileTime: true},\n  },\n  ...shapedSurface({\n    stencil: 'centre',\n    centreTap: 'fast',\n    surface: (frame, params) => {\n      const u = params.uniforms\n      const field = surfaceField(frame, params)\n      const spill = irradianceField(GATHER).sample(params) ?? splat3(0)\n      const glow = emissiveAlpha(exposureTone(spill), 'glow')\n      const cov = local(silhouette(field, u.edgeSoftness), 'cov')\n      return vec4(mix(glow.member('rgb'), u.bodyColor.member('rgb'), cov), mix(glow.member('a'), 1, cov))\n    },\n  }),\n  compute: (params) => irradianceField(GATHER).compute(params),\n})","reach":[{"when":"colored lights spilling around a shape, with shadows","use":"`irradianceField`"},{"when":"a light that the shape's body should block at one point","use":"`shadowVisibility`"},{"when":"a gather that must change every frame","use":"`gatherIrradiance`"}]},"voxels":{"id":"voxels","title":"Voxels","group":"light","module":"std/paint/voxels.ts","access":{"kind":"namespace","name":"voxels"},"curated":true,"words":["voxels.voxelSurface"],"types":["voxels.VoxelFrame","voxels.VoxelGridSpace","voxels.VoxelStyle","voxels.VoxelSurfaceSpec"],"intro":"One word, `voxelSurface`, rebuilds any shape out of blocks. A flat shape becomes chunky\npixel art, an SVG a slab of bricks, a 3D shape a lit voxel model. It asks you for one thing:\nthe color of a single block. For that it hands your `surface` a **voxel frame**, everything\na material needs already worked out: the block's normal, how open its surroundings are, how\nmuch shadow other blocks cast on it, its cell position, a random number that stays fixed to\nthat block, how high and how deep it sits, and how close the pixel is to the face's edge.\n\nSpread `...voxelSurface({...})` into a shape-effect definition next to the standard shape\nprops. The geometry knobs (block size, style, gaps, bevel, camera and light direction) are\nprop refs made with `p('name')`, and changing them rebuilds the model. Your `surface` lights\nthe block with the `materials` words and returns `vec4(rgb, vox.coverage)`. The engine\nresolves which block each pixel sees, anti-aliases the edges and clips to the silhouette.","example":"import {defineShader, p, materials, voxels, math, transformColor, transformPosition} from 'shaders/std'\n\nconst {lambert} = materials\nconst {voxelSurface} = voxels\nconst {add, cos, local, mix, mul, neg, normalize, sin, sub, vec3, vec4} = math\n\nconst DEG = Math.PI / 180\n\n// Any shape as lit blocks: a key light you can orbit, shadows between the blocks, a sky fill in the creases.\nexport const Blocks = defineShader({\n  name: 'Blocks',\n  role: 'shapeEffect',\n  species: 'custom',\n  props: {\n    center: {default: {x: 0.5, y: 0.5}, transform: transformPosition},\n    scale: {default: 1},\n    rotation: {default: 0},\n    voxelSize: {default: 0.035},\n    voxelShape: {default: 'cube', compileTime: true, ui: {type: 'select', options: [{label: 'Cube', value: 'cube'}, {label: 'Rounded', value: 'rounded'}, {label: 'Sphere', value: 'sphere'}]}},\n    gridSpace: {default: 'shape', compileTime: true, ui: {type: 'select', options: [{label: 'Model', value: 'shape'}, {label: 'Screen', value: 'view'}]}},\n    voxelScale: {default: 1},\n    fill: {default: 0},\n    bevel: {default: 0},\n    depth: {default: 0.12},\n    lightAngle: {default: 225},\n    lightElevation: {default: 45},\n    shadows: {default: 0.7},\n    shadowSoftness: {default: 0.3},\n    color: {default: '#e8a24a', transform: transformColor},\n    sky: {default: '#6f86c9', transform: transformColor},\n    shape: {default: JSON.stringify({type: 'sphere3D', radius: 0.35}), ui: {type: 'shape', label: 'Shape'}},\n    shapeSdfUrl: {default: '', compileTime: true},\n    shapeType: {default: '', compileTime: true},\n  },\n  ...voxelSurface({\n    style: p('voxelShape'),\n    gridSpace: p('gridSpace'),\n    voxelSize: p('voxelSize'),\n    fill: p('fill'),\n    voxelScale: p('voxelScale'),\n    bevel: p('bevel'),\n    shadowSoftness: p('shadowSoftness'),\n    lightAngle: p('lightAngle'),\n    lightElevation: p('lightElevation'),\n    depth: p('depth'),\n    surface: (vox, _frame, params) => {\n      const u = params.uniforms\n      // The key light as a vector, from its angle around the canvas and its elevation.\n      const az = mul(u.lightAngle, DEG)\n      const el = mul(u.lightElevation, DEG)\n      const L = local(normalize(vec3(mul(cos(az), cos(el)), mul(sin(az), cos(el)), neg(sin(el)))), 'L')\n      // Key light, cut by the shadow other blocks cast; sky fill, darkened in the creases.\n      const lit = sub(1, mul(vox.shadow, u.shadows))\n      const key = mul(lambert(vox.normal, L, {wrap: 0.12}), lit)\n      const fill = mul(u.sky.member('rgb'), mul(0.5, vox.ao))\n      // A little per-block variation so the bricks read as individual.\n      const tone = mix(0.85, 1.15, vox.cellHash)\n      const rgb = mul(add(mul(u.color.member('rgb'), key), fill), tone)\n      return vec4(rgb, vox.coverage)\n    },\n  }),\n})","reach":[{"when":"a shape as pixel art, bricks or a voxel model","use":"`voxelSurface`"},{"when":"per-block color variation","use":"the frame's per-block random number inside `voxelSurface`"},{"when":"a gradient by height or depth across the model","use":"the frame's height and depth inside `voxelSurface`"}]},"shape":{"id":"shape","title":"Shapes","group":"space","module":"std/shape.ts","access":{"kind":"namespace","name":"shape"},"curated":true,"words":["shape.circle","shape.ellipse","shape.roundedRect","shape.polygon","shape.star","shape.flower","shape.heart","shape.ring","shape.arc","shape.APERTURE_TO_HALF_ANGLE","shape.vesica","shape.crescent","shape.teardrop","shape.cross","shape.trapezoid","shape.parallelogram"],"types":[],"intro":"The 2D shapes: a circle, a star, a heart and a dozen more, each as a function of a point\nthat tells you how far that point is from the shape's edge. The number is a **signed\ndistance**: negative inside, zero on the edge, positive outside. Sizes are in uv, where 1 is\nthe canvas height.\n\nA shape becomes a component through the `shape:` field of `defineShader`. Its `distance`\nreceives the shape-local point (already centred on the `center` prop, aspect-corrected,\nrotated and y down) and `u('name')` for each of your size props, and returns one of these\nwords. `shapeProps` declares those size props. `bounds` names the props that size the\nselection box in the editor. The engine adds the `center`, `rotation`, `color`, stroke and\nsoftness props, the bounding box and the anti-aliased pixel mask, so a shape shader is a\nfew lines. Shapes also combine with `math`: `mix` two distances to morph between them,\n`min` for a union, `max` for an intersection.","example":"import {defineShader, shape, math} from 'shaders/std'\n\nconst {polygon, circle} = shape\n\n// A regular polygon whose corners round off toward a circle.\nexport const Hexagon = defineShader({\n  role: 'shape',\n  name: 'Hexagon',\n  description: 'Regular polygon with adjustable sides and corner rounding',\n  shape: {\n    distance: ({x, y}, u) =>\n      math.mix(polygon(x, y, u('radius'), u('sides')), circle(x, y, u('radius')), u('rounding')),\n    colorDescription: 'Fill color of the polygon',\n    centerDescription: 'Center position of the polygon',\n    bounds: {size: {width: 'radius', height: 'radius'}},\n    shapeProps: {\n      radius: {\n        default: 0.4,\n        description: 'Distance from the center to the middle of each side',\n        ui: {type: 'range', min: 0, max: 1, step: 0.01, label: 'Radius', group: 'Shape'},\n      },\n      sides: {\n        default: 6,\n        description: 'Number of sides (3 = triangle, 4 = square, 6 = hexagon)',\n        ui: {type: 'range', min: 3, max: 12, step: 1, label: 'Sides', group: 'Shape'},\n      },\n      rounding: {\n        default: 0,\n        description: 'Corner rounding: 0 is sharp, 1 becomes a circle',\n        ui: {type: 'range', min: 0, max: 1, step: 0.01, label: 'Rounding', group: 'Shape'},\n      },\n    },\n  },\n})","reach":[{"when":"a disc","use":"`circle`"},{"when":"an oval","use":"`ellipse`"},{"when":"a card, a button, a pill","use":"`roundedRect`"},{"when":"a triangle, a hexagon, any regular polygon","use":"`polygon`"},{"when":"a star or a flower","use":"`star`, `flower`"},{"when":"a donut","use":"`ring`"},{"when":"a pie slice or a gauge arc","use":"`arc` with `APERTURE_TO_HALF_ANGLE`"},{"when":"a heart, a drop, a lens, a moon","use":"`heart`, `teardrop`, `vesica`, `crescent`"},{"when":"a slanted or tapered box","use":"`parallelogram`, `trapezoid`"},{"when":"a plus sign","use":"`cross`"},{"when":"rounded corners on a sharp shape","use":"`mix` toward `circle`"}]},"frames":{"id":"frames","title":"Frames & coordinates","group":"space","module":"std/frames.ts","access":{"kind":"namespace","name":"frames"},"curated":true,"words":["frames.surfaceOf","frames.centredFrame","frames.segmentFrame","frames.direction","frames.directionFrame","frames.sectorFold","frames.rawSurfaceOf"],"types":["frames.CentredFrame","frames.DirectionFrame","frames.SegmentFrame"],"intro":"Frames are how a generator measures its pixels. Instead of raw uv you get named numbers\nready to use: the distance from a centre, how far along and across a line between two\npoints, or the axes of a chosen direction. `surfaceOf` is the starting point: the canvas's\n`uv` (0–1, y down), its size in pixels and its aspect ratio. Every frame is aspect-corrected,\nso 1 is the canvas height and a circle stays round on a wide canvas.\n\nBuild a frame from your position props with `p('name')`, call it with `params` and the\nsurface, and pull out the numbers you need. Shape them with `math` and hand the result to a\npalette, a `light` word or `compose` to make the pixel's color. `direction` and\n`directionFrame` take an angle in degrees, 0 pointing right and turning clockwise on screen.","example":"import {defineShader, p, frames, math, transformColor, transformPosition} from 'shaders/std'\n\nconst {surfaceOf, segmentFrame} = frames\nconst {div, exp, mul, neg, smoothstep, sub, vec4} = math\n\n// A bar of light between two draggable points: bright on the line, fading across it, feathered at both ends.\nexport const LightBar = defineShader({\n  name: 'LightBar',\n  props: {\n    from: {default: {x: 0.2, y: 0.5}, transform: transformPosition},\n    to: {default: {x: 0.8, y: 0.5}, transform: transformPosition},\n    color: {default: '#7ad7ff', transform: transformColor},\n    width: {default: 0.05},\n    feather: {default: 0.1},\n  },\n  paint: (params) => {\n    const u = params.uniforms\n    const {along, across, length} = segmentFrame({from: p('from'), to: p('to')})(params, surfaceOf(params))\n    const profile = exp(neg(div(mul(across, across), mul(u.width, u.width))))\n    const ends = mul(smoothstep(0, u.feather, along), smoothstep(0, u.feather, sub(length, along)))\n    const bar = mul(profile, ends)\n    return vec4(mul(u.color.member('rgb'), bar), bar)\n  },\n})","reach":[{"when":"the canvas uv, size and aspect at the top of a paint","use":"`surfaceOf`"},{"when":"a pattern anchored to the whole canvas, not the layer's box","use":"`rawSurfaceOf`"},{"when":"the distance from a draggable centre","use":"`centredFrame`"},{"when":"the position along and across a line between two points","use":"`segmentFrame`"},{"when":"a light or flow angle as a vector","use":"`direction`"},{"when":"a grain, weave or streak running at an angle","use":"`directionFrame`"},{"when":"a kaleidoscope","use":"`sectorFold`"}]},"warps":{"id":"warps","title":"Warps","group":"space","module":"std/warps.ts","access":{"kind":"namespace","name":"warps"},"curated":true,"words":["warps.twirl","warps.bulge","warps.mirrorLine","warps.flip","warps.kaleidoscope","warps.perspective","warps.cornerPin","warps.bend","warps.stretch","warps.wave","warps.flowNoise","warps.toPolar","warps.fromPolar","warps.concentricRings","warps.barOffset","warps.slicedSlide","warps.liquidDisplace","warps.gridCellDisplace","warps.displaceByLayer"],"types":[],"intro":"Words for the `map:` field. A **warp** is a component that moves the pixels of the layer\ninside it (the child): for every pixel on screen, the map says where in the child to look.\nTwisting, bulging, mirroring, tilting and wiping are all warps. Positions are in uv (0–1\nacross the canvas, y down) and angles are in degrees.\n\nA definition with a `map:` is a warp, nothing else to declare. Most maps can look outside\nthe child, so the definition also says what happens there with `edges`: the default reads\nan `edges` prop holding `'stretch'`, `'transparent'`, `'mirror'` or `'wrap'`; a number bakes\none mode in (`edges: 1` is transparent); `'none'` is for a map that never leaves the child.\n`resample` picks the texture filter, `'catmullRom'` (the default, sharp under\nmagnification) or `'bilinear'`. Animated warps read the layer's clock, so declare\n`animatedTime: {speed: 'speed'}`.\n\nOne word is not a map: `displaceByLayer` reads a second layer, so it goes in `effect:`.","example":"import {defineShader, p, warps, transformPosition, transformEdges} from 'shaders/std'\n\n// Twirl: twist the child around a draggable center, more the farther from it.\nexport const Twirl = defineShader({\n  name: 'Twirl',\n  props: {\n    center: {default: {x: 0.5, y: 0.5}, transform: transformPosition},\n    intensity: {default: 1, ui: {type: 'range', min: -5, max: 5, step: 0.1}},\n    edges: {\n      default: 'stretch',\n      transform: transformEdges,\n      compileTime: true,\n      ui: {type: 'select', options: [{label: 'Stretch', value: 'stretch'}, {label: 'Transparent', value: 'transparent'}, {label: 'Mirror', value: 'mirror'}, {label: 'Wrap', value: 'wrap'}]},\n    },\n  },\n  map: warps.twirl({center: p('center'), intensity: p('intensity')}),\n})","reach":[{"when":"twist the picture around a point","use":"`twirl`"},{"when":"a magnifying glass or a pinch","use":"`bulge`"},{"when":"reflect one half onto the other","use":"`mirrorLine`"},{"when":"flip horizontally or vertically","use":"`flip` with `edges: 'none'`"},{"when":"mirrored pie slices","use":"`kaleidoscope`"},{"when":"a card tilted in 3D","use":"`perspective`"},{"when":"pin the corners anywhere","use":"`cornerPin`"},{"when":"a curved screen","use":"`bend`"},{"when":"pull the picture out along one direction","use":"`stretch`"},{"when":"a travelling ripple","use":"`wave`"},{"when":"a slow liquid drift","use":"`flowNoise`"},{"when":"unroll the picture into a circle, or roll it back","use":"`toPolar`, `fromPolar`"},{"when":"rings that each spin on their own","use":"`concentricRings`"},{"when":"a glitch of shifted bars","use":"`barOffset`"},{"when":"a wipe transition that shreds the picture","use":"`slicedSlide` with `edges: 1`"},{"when":"distortion driven by your own simulation","use":"`liquidDisplace`, `gridCellDisplace`"},{"when":"another layer's pixels as a displacement map","use":"`displaceByLayer` in `effect:`"}]},"mask":{"id":"mask","title":"Masks","group":"space","module":"std/mask.ts","access":{"kind":"namespace","name":"mask"},"curated":true,"words":["mask.radialMask","mask.softDisc","mask.crispFill","mask.crispStroke","mask.softBand"],"types":[],"intro":"A mask is coverage: a number from 0 to 1 at every pixel that says how much of something\nshows there. Multiply it into a color's alpha to cut a shape out of a paint, or hand it to\nan effect as its amount so the effect fades in across the canvas. Most masks start from a\ndistance, how far this pixel is from a center, a line or a shape's edge, and the mask word\nturns that distance into an edge. `softDisc` and `softBand` give soft, styled edges for orbs\nand curtains. `crispFill` and `crispStroke` give clean anti-aliased fills and outlines from a\nsigned distance, such as one of the `shape` words. `radialMask` is the ready-made one for\neffects: 0 at a center, 1 past a radius, plugged straight into an effect's amount.","example":"import {defineShader, p, uniformOf, math, mask, frames, transformColor, transformPosition} from 'shaders/std'\n\nconst {add, sub, mul, div, clamp, local, vec4} = math\n\n// Orb: a soft glowing disc with a crisp ring just outside it, over a transparent background.\nexport const Orb = defineShader({\n  name: 'Orb',\n  props: {\n    center: {default: {x: 0.5, y: 0.5}, transform: transformPosition},\n    color: {default: '#7dd3fc', transform: transformColor},\n    ringColor: {default: '#ffffff', transform: transformColor},\n    radius: {default: 0.2},\n    softness: {default: 0.5},\n  },\n  paint: (params) => {\n    const u = (name: string) => uniformOf(p(name), params)\n    const surface = frames.surfaceOf(params)\n    const {dist} = frames.centredFrame({center: p('center')})(params, surface)\n    const px = div(1, surface.viewport.member('y')) // one device pixel, in uv\n    const body = local(mask.softDisc({\n      dist,\n      radius: u('radius'),\n      softness: {width: mul(u('softness'), 0.3), curve: add(mul(u('softness'), 2), 0.5)},\n    }), 'body')\n    // A signed distance to a circle a little larger than the orb: negative inside, zero on the ring.\n    const ring = local(mask.crispStroke({distance: sub(dist, mul(u('radius'), 1.3)), width: 0.006, footprint: px}), 'ring')\n    const rgb = add(mul(u('color').member('rgb'), body), mul(u('ringColor').member('rgb'), ring))\n    return vec4(rgb, clamp(add(body, ring), 0, 1))\n  },\n})","reach":[{"when":"an effect that is strongest at the edges and clear in the middle","use":"`radialMask` as the effect's amount"},{"when":"a soft glowing disc, orb or dot","use":"`softDisc`"},{"when":"a clean filled shape from a signed distance","use":"`crispFill`"},{"when":"an outline of a shape, or a drawn curve of a given width","use":"`crispStroke`"},{"when":"a band along a line, sharp on one side and trailing off on the other","use":"`softBand`"}]},"math":{"id":"math","title":"Math","group":"space","module":"std/math.ts","access":{"kind":"namespace","name":"math"},"curated":true,"words":["math.add","math.sub","math.mul","math.div","math.mix","math.smoothstep","math.clamp","math.local","math.vec4","math.vec3","math.vec2","math.splat3","math.length","math.distance","math.abs","math.fract","math.floor","math.min","math.max","math.sin","math.cos","math.step","math.pow","math.exp","math.sqrt","math.neg","math.select","math.lt","math.gt","math.le","math.ge","math.atan2","math.dot","math.normalize","math.rotate2","math.gaussBell","math.softPlus","math.hashPhase","math.sign","math.ceil","math.log","math.exp2","math.tan","math.cross","math.reflect","math.refract","math.float"],"types":[],"intro":"The arithmetic you write your own paint with. Every word takes expressions or plain numbers\nand returns an expression the GPU works out once per pixel: `mul(color, coverage)` fades a\ncolor, `smoothstep(a, b, dist)` turns a distance into a soft mask, `mix(a, b, t)` blends two\ncolors. Props reach you as `params.uniforms.<name>` (or `uniformOf(p('name'), params)`), a\nframe gives you the coordinate, and the words compose by nesting until you return a `vec4`\ncolor from `paint:`. Wrap anything you read twice in `local` so it is computed once.","example":"import {defineShader, p, uniformOf, animatedTime, math, frames, transformColor, transformPosition} from 'shaders/std'\n\nconst {add, sub, mul, sin, smoothstep, mix, splat3, local, vec4} = math\n\n// Rings: concentric waves spreading from a draggable center, blended between two colors.\nexport const Rings = defineShader({\n  name: 'Rings',\n  animatedTime: {speed: 'speed'},\n  props: {\n    center: {default: {x: 0.5, y: 0.5}, transform: transformPosition},\n    colorA: {default: '#0b1026', transform: transformColor},\n    colorB: {default: '#7dd3fc', transform: transformColor},\n    frequency: {default: 24},\n    softness: {default: 0.25},\n    speed: {default: 1},\n  },\n  paint: (params) => {\n    const u = (name: string) => uniformOf(p(name), params)\n    const {dist} = frames.centredFrame({center: p('center')})(params, frames.surfaceOf(params))\n    // A −1..1 wave along the distance, moved into 0..1 and computed once.\n    const wave = local(add(mul(sin(sub(mul(dist, u('frequency')), animatedTime(params))), 0.5), 0.5), 'wave')\n    // softness 0 is a hard ring, 0.5 a smooth gradient between rings.\n    const band = smoothstep(sub(0.5, u('softness')), add(0.5, u('softness')), wave)\n    const rgb = mix(u('colorA').member('rgb'), u('colorB').member('rgb'), splat3(band))\n    return vec4(rgb, 1)\n  },\n})","reach":[{"when":"combining numbers, colors or coordinates","use":"`add`, `sub`, `mul`, `div`"},{"when":"blending two colors or values by an amount","use":"`mix` with `splat3` for colors"},{"when":"a soft edge from a distance","use":"`smoothstep`; `step` for a hard one"},{"when":"keeping a value in range","use":"`clamp`, `min`, `max`"},{"when":"a value you read more than once","use":"`local`"},{"when":"the distance from a center or between points","use":"`length`, `distance`"},{"when":"a pattern that repeats across the canvas","use":"`fract` and `floor` on a scaled coordinate"},{"when":"a wave or a rotation over time","use":"`sin`, `cos`, `rotate2`"},{"when":"an angle around a center","use":"`atan2`"},{"when":"a smooth falloff or a bell shape","use":"`exp`, `gaussBell`"},{"when":"a hinge that bends instead of creasing","use":"`softPlus`"},{"when":"picking one of two values per pixel","use":"`select` with `lt`, `gt`, `le`, `ge`"},{"when":"several motions that must not move in step","use":"`hashPhase`"},{"when":"the final color","use":"`vec4` from a `vec3` and an alpha"}]},"motion":{"id":"motion","title":"Motion & timing","group":"space","module":"std/motion.ts","access":{"kind":"namespace","name":"motion"},"curated":true,"words":["motion.oscillating","motion.pulsing","motion.cycle","motion.cycleSeed","motion.oscillate","motion.pulseTrain","motion.phase","motion.easeInOut","motion.easeOut","motion.easeIn","motion.flashes"],"types":[],"intro":"Words that turn a clock into rhythm. The clock is a number of seconds: declare\n`animatedTime: {speed: 'speed'}` on the definition and read the layer's clock with\n`animatedTime(params)`, and a speed prop and a pause button come for free. `cycle` splits\nthat clock into \"which pass is this\" and \"how far through it are we\", `cycleSeed` gives each\npass a stable random number, `oscillate` and `pulseTrain` shape it into a sweep or a blink,\nand the easings bend a 0..1 progress. The result is a number you multiply into a color, a\nradius or a coordinate.\n\n`oscillating` and `pulsing` are the same shapes packaged as a value you can drop into any\nslot that takes a prop, on a warp or an effect, so a knob moves on its own with no math in\nyour definition.","example":"import {defineShader, p, uniformOf, animatedTime, math, motion, transformColor} from 'shaders/std'\n\nconst {sub, mul, mix, splat3, vec4} = math\n\n// Flash: every `period` seconds the canvas lights up in a new random blend of two colors and fades out.\nexport const Flash = defineShader({\n  name: 'Flash',\n  animatedTime: {speed: 'speed'},\n  props: {\n    colorA: {default: '#ff7e5f', transform: transformColor},\n    colorB: {default: '#7dd3fc', transform: transformColor},\n    period: {default: 2},\n    speed: {default: 1},\n  },\n  paint: (params) => {\n    const u = (name: string) => uniformOf(p(name), params)\n    const t = animatedTime(params)\n    const {progress, index} = motion.cycle(t, u('period'))\n    const fade = motion.easeOut(sub(1, progress)) // bright at the start of each pass, then out\n    const pick = motion.cycleSeed(index) // one stable random blend per pass\n    const breathe = motion.oscillate(t, {rate: 0.5, min: 0.7, max: 1})\n    const rgb = mix(u('colorA').member('rgb'), u('colorB').member('rgb'), splat3(pick))\n    return vec4(mul(rgb, mul(fade, breathe)), 1)\n  },\n})","reach":[{"when":"a warp or effect knob that sweeps back and forth by itself","use":"`oscillating` in the slot"},{"when":"a knob that switches on and off in a rhythm","use":"`pulsing` in the slot"},{"when":"something happens every N seconds","use":"`cycle`, which gives progress and index"},{"when":"each pass should look different but hold still while it lasts","use":"`cycleSeed` on the cycle's index"},{"when":"a smooth breathing value between two numbers","use":"`oscillate`"},{"when":"a blink or strobe with a controllable on time","use":"`pulseTrain`"},{"when":"just the 0..1 position inside a repeating cycle","use":"`phase`"},{"when":"a progress that accelerates, settles or glides","use":"`easeIn`, `easeOut`, `easeInOut`"}]},"signal":{"id":"signal","title":"Signals","group":"space","module":"std/signal.ts","access":{"kind":"namespace","name":"signal"},"curated":true,"words":["signal.pointer","signal.pointerSpeed","signal.cpuHash01"],"types":["signal.PointerSignal","signal.PointerSpeedSignal"],"intro":"Live inputs a simulation reads every frame: where the pointer is and how fast it is moving.\nYou never read the numbers yourself. You hand a signal to a simulation step, such as the\nsplat step of a grid simulation, and the engine tracks the pointer, measures its speed in\nuv per second, and feeds both in. `pointer` carries one choice, the teleport guard, which\nstops a cursor that jumps into the canvas from slamming the field. `pointerSpeed` caps the\nspeed so a fast flick cannot overdrive it. Both are also exported at the top level of\n`shaders/std`.\n\n`cpuHash01` is the odd one out: a stable random number from a seed, worked out once in\nJavaScript, for constants a paint needs per item rather than per pixel.","example":"import {defineShader, p, simulate, op, signal, displaceBy, transformEdges} from 'shaders/std'\n\n// A height field stirred at the pointer, harder the faster it moves, that settles when left alone.\nconst waves = simulate.grid({\n  resolution: 128,\n  history: 2,\n  step: [\n    op.wave({damping: p('decay')}),\n    op.splat({at: signal.pointer({teleportGuard: 'on'}), amount: signal.pointerSpeed({max: 2}), radius: p('radius')}),\n  ],\n  derive: {displacement: op.gradient()},\n  rest: {settlesWhen: 'derived-from-damping'},\n})\n\n// Ripples: the field's slope pushes the layer inside around, with a little color fringing.\nexport const Ripples = defineShader({\n  name: 'Ripples',\n  props: {\n    intensity: {default: 10},\n    decay: {default: 10},\n    radius: {default: 0.5},\n    chromaticSplit: {default: 1},\n    edges: {default: 'stretch', transform: transformEdges, compileTime: true},\n  },\n  effect: displaceBy(waves.output('displacement'), {\n    strength: p('intensity'),\n    chromatic: p('chromaticSplit'),\n    edges: p('edges'),\n  }),\n})","reach":[{"when":"a simulation should be stirred where the cursor is","use":"`pointer` as the splat step's position"},{"when":"stirring harder the faster the cursor moves","use":"`pointerSpeed` as the splat step's amount"},{"when":"a cursor entering the canvas must not cause a splash","use":"`pointer` with the teleport guard on (the default)"},{"when":"a stable random size, speed or offset per item, computed once","use":"`cpuHash01`"}]},"filter":{"id":"filter","title":"Filter effects","group":"effects","module":"std/filter.ts","access":{"kind":"top-level"},"curated":true,"words":["filter.tintToward","filter.paintThrough","filter.displaceBy","filter.pointwise","filter.gather"],"types":["filter.DisplaceByEffect","filter.TintTowardEffect"],"intro":"Words for the `effect:` field. A **filter** is a component that changes the layer inside it\n(the child). A definition with an `effect:` is a filter, nothing else to declare.\n\nThere are two kinds of effect. A **pointwise** effect edits the child's color at this pixel\nand nothing else. It is cheap and keeps the child's alpha. A **gather** effect first draws\nthe child to a texture so it can read the child's other pixels: neighbours for a blur, a\nshifted position for a distortion. It costs one extra render of the child. `tintToward` and\n`paintThrough` are pointwise, `displaceBy` is a gather, and `pointwise` and `gather` let you\nwrite your own. The many ready-made effects (blurs, color grades, stylize looks) live in the\ncategories after this one and drop into the same `effect:` field.","example":"import {defineShader, p, tintToward, radialMask, isZero, transformColor, transformPosition} from 'shaders/std'\n\n// Vignette: tint the frame toward a color, more toward the edges, nothing at the center.\nexport const Vignette = defineShader({\n  name: 'Vignette',\n  props: {\n    color: {default: '#000000', transform: transformColor},\n    center: {default: {x: 0.5, y: 0.5}, transform: transformPosition},\n    radius: {default: 0.5},\n    falloff: {default: 0.5},\n    intensity: {default: 1},\n  },\n  effect: tintToward(p('color'), {\n    amount: radialMask({center: p('center'), radius: p('radius'), falloff: p('falloff')}).times(p('intensity')),\n  }),\n  identityWhen: isZero('intensity'),\n})","reach":[{"when":"tint or darken the child, by a mask or a slider","use":"`tintToward`"},{"when":"a gradient or pattern showing through text","use":"`paintThrough`"},{"when":"a ripple or fluid distortion driven by a simulation","use":"`displaceBy`"},{"when":"your own color math on the child's pixel","use":"`pointwise`"},{"when":"your own effect that reads the child's neighbours","use":"`gather`"}]},"effects.color":{"id":"effects.color","title":"Color effects","group":"effects","module":"std/effects/color.ts","access":{"kind":"namespace","name":"effects.color"},"curated":true,"words":["effects.color.saturate","effects.color.vibrance","effects.color.hueRotate","effects.color.brightnessContrast","effects.color.exposure","effects.color.grayscale","effects.color.invert","effects.color.solarize","effects.color.posterize","effects.color.tint","effects.color.duotone","effects.color.tritone","effects.color.gradientMap","effects.color.filmGrain","effects.color.isolines","effects.color.lutAtlasGrade"],"types":[],"intro":"Words that recolor the layer inside them, one pixel at a time. Each returns a **pointwise\neffect** for a filter definition's `effect:` field: it reads the child's color at this pixel\nand returns the adjusted color, with alpha kept. Nothing is sampled from neighbouring\npixels, so these filters are cheap and stack freely. Pass props with `p('name')`; the units\nare the ones the word names (degrees, 0–1 amounts, a multiplier around 1).\n\nMost adjustments have a value that changes nothing (`saturate` at 1, `hueRotate` at 0,\n`solarize` at strength 0). Declare it with `identityWhen` so the filter is skipped when it is\ndoing nothing. The looks that pick a code path from a select or checkbox (`tint`'s\n`preserveLuminosity`, `duotone`'s `colorSpace`, `gradientMap`'s `palette`, `isolines`'\n`source` and `colorMode`) need that prop declared `compileTime: true`.","example":"import {defineShader, p, effects, isZero, transformColor, transformColorSpace, colorSpaceOptions} from 'shaders/std'\n\nconst {duotone} = effects.color\n\n// Two-color print: shadows in one color, highlights in another, mixed in the chosen color space.\nexport const Duotone = defineShader({\n  name: 'Duotone',\n  props: {\n    colorA: {default: '#1a0b2e', transform: transformColor},\n    colorB: {default: '#f9ed69', transform: transformColor},\n    blend: {default: 0.5, ui: {type: 'range', min: 0, max: 1, step: 0.01}},\n    colorSpace: {default: 'oklch', transform: transformColorSpace, compileTime: true, ui: {type: 'select', options: colorSpaceOptions}},\n  },\n  effect: duotone({colorA: p('colorA'), colorB: p('colorB'), blend: p('blend'), colorSpace: p('colorSpace')}),\n  missingChildMessage: 'Duotone needs a child to recolor.',\n})","reach":[{"when":"more or less color","use":"`saturate`, or `vibrance` to protect skin tones"},{"when":"a different hue","use":"`hueRotate`"},{"when":"brighter, darker, punchier","use":"`brightnessContrast`, `exposure`"},{"when":"black and white","use":"`grayscale`"},{"when":"a negative or a darkroom flip of the highlights","use":"`invert`, `solarize`"},{"when":"flat poster bands","use":"`posterize`"},{"when":"a color wash over everything","use":"`tint`"},{"when":"two or three colors mapped to brightness","use":"`duotone`, `tritone`"},{"when":"a full gradient mapped to brightness, animated or not","use":"`gradientMap`"},{"when":"film grain","use":"`filmGrain`"},{"when":"contour lines traced from brightness or alpha","use":"`isolines`"},{"when":"a measured film-stock lookup table in a `gpu:` fragment","use":"`lutAtlasGrade`"}]},"effects.blurs":{"id":"effects.blurs","title":"Blurs, glows & screens","group":"effects","module":"std/effects/blurs.ts","access":{"kind":"namespace","name":"effects.blurs"},"curated":true,"words":["effects.blurs.gaussianBlur","effects.blurs.motionBlur","effects.blurs.blurPath","effects.blurs.progressiveBlur","effects.blurs.tiltShift","effects.blurs.bloom","effects.blurs.bokehDefocus","effects.blurs.channelBlur","effects.blurs.scatter","effects.blurs.sharpen","effects.blurs.pixelate","effects.blurs.gatherStack","effects.blurs.childTap","effects.blurs.shadowOffset","effects.blurs.silhouetteCoverage","effects.blurs.shadowComposite","effects.blurs.rgbSplit","effects.blurs.adjust","effects.blurs.scanlines","effects.blurs.phosphorMask","effects.blurs.vignetteOverlay","effects.blurs.opaque","effects.blurs.tapeWarp","effects.blurs.chromaSmearTaps","effects.blurs.beatPulse","effects.blurs.glitchFrame","effects.blurs.glitchRgbSplit","effects.blurs.colorBarFills","effects.blurs.distortedScanlines","effects.blurs.sphereFrame","effects.blurs.crispTap","effects.blurs.rimLit","effects.blurs.fluteFrame","effects.blurs.refractedTaps","effects.blurs.fluteHighlight","effects.blurs.peelFrame","effects.blurs.peelCompose","effects.blurs.curlShading","effects.blurs.curlSheen","effects.blurs.overhangShadow","effects.blurs.revealShadow","effects.blurs.mirrorAcrossRow","effects.blurs.depthRampBlur","effects.blurs.planarReflection","effects.blurs.screenedBloom","effects.blurs.frameSway","effects.blurs.blurPath.linear","effects.blurs.blurPath.orbit","effects.blurs.blurPath.zoom","effects.blurs.brightestNear"],"types":["effects.blurs.BlurPathSpec","effects.blurs.ComputeBackedEffect","effects.blurs.GatherFrame","effects.blurs.MotionBlurDetailSlots","effects.blurs.OverlayStage","effects.blurs.PeelShade","effects.blurs.SampleStage","effects.blurs.VariableBlurDetailSlots"],"intro":"Effects that read the layer inside them (the child) as a texture and rebuild it: blurs, glows,\nsharpening, pixelation, shadows, retro screens, glitches, fluted glass, page peels and mirror\nfloors. A **pointwise** effect edits the child's color at one pixel; the words here are\n**gather** effects, which sample the child at other pixels too, so they can smear, spread and\ndisplace it. Every word here goes in a filter definition's `effect:` field, or spreads into a\ndefinition when it needs its own pass over the child first.\n\nFinished effects are one call: `gaussianBlur`, `motionBlur`, `bloom`, `pixelate`, `sharpen`,\n`scatter`, `bokehDefocus`. Stacked looks are assembled with `gatherStack` from a **sampling\nstage** (how the child is first read: `rgbSplit`, `chromaSmearTaps`, `crispTap`, `childTap`)\nand ordered **overlay stages** that edit the result (`scanlines`, `adjust`, `rimLit`,\n`shadowComposite`). When several stages need the same geometry, build a **frame** once\n(`sphereFrame`, `glitchFrame`, `fluteFrame`, `peelFrame`) and hand it to each of them.\n\nBlurs that need a separate pass (`gaussianBlur`, `channelBlur`, `progressiveBlur`,\n`tiltShift`, `bloom`, `bokehDefocus`) return both halves at once. Spread them into a definition\nthat declares `species: 'custom'`, `requiresRTT: true` and `requiresChild: true` instead of\nusing `effect:`.","example":"import {defineShader, p, effects, transformColor, transformBoolean} from 'shaders/std'\n\nconst {gatherStack, childTap, shadowOffset, silhouetteCoverage, shadowComposite} = effects.blurs\n\n// A soft drop shadow: read the child's silhouette offset in a compass direction, blur it,\n// tint it, and place it behind the child.\nexport const SoftShadow = defineShader({\n  name: 'SoftShadow',\n  props: {\n    color: {default: '#000000', transform: transformColor},\n    distance: {default: 0.1},\n    angle: {default: 135},\n    blur: {default: 5},\n    intensity: {default: 0.5},\n    cutout: {default: false, transform: transformBoolean, compileTime: true},\n  },\n  effect: gatherStack(childTap(), [\n    shadowComposite({\n      coverage: silhouetteCoverage({\n        at: shadowOffset({angle: p('angle'), distance: p('distance')}),\n        blur: p('blur'),\n      }),\n      color: p('color'),\n      intensity: p('intensity'),\n      cutout: p('cutout'),\n    }),\n  ]),\n})","reach":[{"when":"a soft, even blur","use":"`gaussianBlur`"},{"when":"a directional streak, a spin, or a zoom blur","use":"`motionBlur` with `blurPath`"},{"when":"a blur that fades in across the canvas","use":"`progressiveBlur`"},{"when":"a band in focus with blurred edges","use":"`tiltShift`"},{"when":"a glow around bright parts","use":"`bloom`"},{"when":"a lens blur with shaped highlight discs","use":"`bokehDefocus`"},{"when":"a chromatic fringe from blurring channels apart","use":"`channelBlur`"},{"when":"grainy diffusion rather than a smooth blur","use":"`scatter`"},{"when":"crisper detail","use":"`sharpen`"},{"when":"a pixel grid, with gaps or rounded cells","use":"`pixelate`"},{"when":"a soft shadow behind the child","use":"`childTap` + `shadowOffset`, `silhouetteCoverage`, `shadowComposite`"},{"when":"an old CRT monitor","use":"`rgbSplit` + `adjust`, `scanlines`, `phosphorMask`, `vignetteOverlay`, `opaque`"},{"when":"a worn VHS tape","use":"`chromaSmearTaps` with `tapeWarp`, then `beatPulse`"},{"when":"digital glitch bursts","use":"`glitchFrame` + `glitchRgbSplit`, `colorBarFills`, `distortedScanlines`"},{"when":"the child bulging on a sphere","use":"`sphereFrame` + `crispTap`, `rimLit`"},{"when":"reeded or fluted glass over the child","use":"`fluteFrame` + `refractedTaps`, `fluteHighlight`"},{"when":"a page curling up from a corner","use":"`peelFrame` + `peelCompose` with the four peel shades"},{"when":"a mirror floor with a fading, softening reflection","use":"`mirrorAcrossRow`, `depthRampBlur`, `planarReflection`"},{"when":"film halation or phosphor bloom over a graded color","use":"`screenedBloom`"},{"when":"a projector-gate wobble","use":"`frameSway`"}]},"effects.stylize":{"id":"effects.stylize","title":"Stylize","group":"effects","module":"std/effects/stylize.ts","access":{"kind":"namespace","name":"effects.stylize"},"curated":true,"words":["effects.stylize.wash","effects.stylize.kuwahara","effects.stylize.bleedWarp","effects.stylize.paperGrain","effects.stylize.chalkSketch","effects.stylize.sobelTaps","effects.stylize.linework","effects.stylize.glyphTint","effects.stylize.glyphFor","effects.stylize.charGrid","effects.stylize.cellSample"],"types":["effects.stylize.FragmentStage","effects.stylize.GatherStage","effects.stylize.KuwaharaBrush","effects.stylize.SobelRing"],"intro":"Hand-made looks for the layer inside: a watercolor wash, a chalk drawing on a board, ASCII\ncharacters, and copper-plate line engraving. Each look is a recipe of two or three words. A\n**stage** is one piece of the look that reads the layer once it is rendered to a texture (a\nwobbled sample position, a brush, a ring of edge samples, a character grid). Build a stage once\nand hand the same one to every word that needs it, so they agree. The closing word (`wash`,\n`chalkSketch`, `linework`) returns the effect for `effect:`. The ASCII look closes with\n`glyphTint`, which returns a fragment for `gpu.fragment` because it also needs a character\natlas drawn on the CPU.\n\nThe output is straight alpha. Watercolor, chalk and ASCII keep the alpha of the layer inside;\nengraving replaces the layer, so its definition sets `blendWithChildren: false`.","example":"import {defineShader, p, effects, crosses, transformColor} from 'shaders/std'\n\nconst {bleedWarp, kuwahara, paperGrain, wash} = effects.stylize\n\n// One bleed stage, shared by the brush and the wash so the taps and the original sample agree.\nconst wet = bleedWarp({amount: p('bleed')})\n\n// Watercolor: soft colour patches over paper grain, mixed back toward the original by strength.\nexport const Watercolor = defineShader({\n  name: 'Watercolor',\n  props: {\n    radius: {default: 3, compileTime: true},\n    bleed: {default: 1, recompile: crosses(0)},\n    strength: {default: 1, recompile: crosses(0)},\n    paper: {default: 0.35, recompile: crosses(0)},\n    paperColor: {default: '#fbf7ec', transform: transformColor},\n  },\n  effect: wash({\n    at: wet,\n    brush: kuwahara({at: wet, radius: p('radius')}),\n    grain: paperGrain({amount: p('paper')}),\n    paper: p('paper'),\n    paperColor: p('paperColor'),\n    strength: p('strength'),\n  }),\n})","reach":[{"when":"a painted, softened watercolor version of the layer","use":"`wash` with `kuwahara`, `bleedWarp`, `paperGrain`"},{"when":"paint that bleeds past hard edges","use":"`bleedWarp`, shared as the sample position by `kuwahara` and `wash`"},{"when":"a chalk drawing with outlines and cross-hatching","use":"`chalkSketch` with `sobelTaps`"},{"when":"the layer redrawn as text characters","use":"`charGrid`, `cellSample`, `glyphFor`, `glyphTint`"},{"when":"an engraved, banknote-style line drawing","use":"`linework`"}]},"effects.lens":{"id":"effects.lens","title":"Lens","group":"effects","module":"std/effects/lens.ts","access":{"kind":"namespace","name":"effects.lens"},"curated":true,"words":["effects.lens.chromaticFan","effects.lens.spectralLens"],"types":["effects.lens.SpectralLensSlots"],"intro":"Words for the color fringing a real lens leaves behind: the red, green and blue copies of\nthe layer inside pushed apart, or fanned out into a whole spectrum. Because they read the\nchild at several places per pixel, these are **gather effects**: the child is rendered to a\ntexture first and the effect samples it. `chromaticFan` is a sampling stage; hand it to\n`gatherStack` (from `effects.blurs`) to turn it into the `effect:` of a filter definition,\noptionally followed by more stages. `spectralLens` is a complete fragment for a `gpu:`\ndefinition, with the lens warp, focus zones and grain built in.\n\nAngles are in degrees and go through `transformAngle`. Strengths are 0–1.","example":"import {defineShader, p, effects, transformAngle} from 'shaders/std'\n\nconst {chromaticFan} = effects.lens\nconst {gatherStack} = effects.blurs\n\n// Chromatic aberration: red and blue pulled apart along an angle, green held in place.\nexport const Fringe = defineShader({\n  name: 'Fringe',\n  props: {\n    strength: {default: 0.2, ui: {type: 'range', min: 0, max: 1, step: 0.01}},\n    angle: {default: 0, transform: transformAngle, ui: {type: 'range', min: 0, max: 360, step: 1}},\n    redOffset: {default: -1, ui: {type: 'range', min: -2, max: 2, step: 0.1}},\n    greenOffset: {default: 0, ui: {type: 'range', min: -2, max: 2, step: 0.1}},\n    blueOffset: {default: 1, ui: {type: 'range', min: -2, max: 2, step: 0.1}},\n  },\n  effect: gatherStack(chromaticFan({\n    strength: p('strength'),\n    angle: p('angle'),\n    red: p('redOffset'),\n    green: p('greenOffset'),\n    blue: p('blueOffset'),\n  }), []),\n  missingChildMessage: 'Fringe needs a child to split.',\n})","reach":[{"when":"a subtle RGB split along one direction","use":"`chromaticFan` inside `gatherStack`"},{"when":"the full prism look: many color copies, a bulging lens, swirl and grain","use":"`spectralLens`"}]},"effects.reveal":{"id":"effects.reveal","title":"Reveals & wipes","group":"effects","module":"std/effects/reveal.ts","access":{"kind":"namespace","name":"effects.reveal"},"curated":true,"words":["effects.reveal.reveal","effects.reveal.coverage","effects.reveal.coverage.directional","effects.reveal.coverage.radial","effects.reveal.coverage.angular","effects.reveal.coverage.cells","effects.reveal.coverage.noise","effects.reveal.Coverage","effects.reveal.BandedCoverage","effects.reveal.CellCoverage"],"types":["effects.reveal.AngularDirection","effects.reveal.RevealSlots"],"intro":"Words that make the layer inside disappear in a chosen order: a straight edge, a growing\ncircle, a clock sweep, a grid of blocks, a noise dissolve. Every wipe is the same effect,\n`reveal`, fed a different **coverage**: a number per pixel from 0 to 1 that says when that\npixel goes (0 first, 1 last). As the `progress` prop climbs from 0 to 1, pixels whose\ncoverage falls below it fade out across a soft front `feather` wide.\n\nThe `coverage` words produce the ordering. Their modifiers reshape it: `.folded()` turns one\nedge into two opening outward, `.tiled(n)` repeats it into strips, `.bands(n)` cuts it into\nbands that vanish whole, and `.shuffled()` randomizes the band or cell order. Read a wipe from\nthe inside out: the coverage, its modifiers, then `reveal` with the three timing props.\n\nOnly the child's alpha changes, so a wipe composes over anything behind it and costs one\npointwise pass. `progress` has no clock of its own; drive it from a prop.","example":"import {defineShader, p, effects, transformBoolean} from 'shaders/std'\n\nconst {reveal, coverage} = effects.reveal\n\n// Random bars: a directional wipe cut into bars that vanish in a fixed shuffled order.\nexport const RandomBars = defineShader({\n  name: 'RandomBars',\n  props: {\n    progress: {default: 0.5, ui: {type: 'range', min: 0, max: 1, step: 0.01}},\n    angle: {default: 90, ui: {type: 'range', min: 0, max: 360, step: 1}},\n    barCount: {default: 24, ui: {type: 'range', min: 2, max: 100, step: 1}},\n    softness: {default: 0.1, ui: {type: 'range', min: 0, max: 1, step: 0.01}},\n    invert: {default: false, transform: transformBoolean, ui: {type: 'checkbox'}},\n  },\n  effect: reveal({\n    coverage: coverage.directional(p('angle')).bands(p('barCount')).shuffled(),\n    progress: p('progress'),\n    feather: p('softness'),\n    invert: p('invert'),\n  }),\n  missingChildMessage: 'RandomBars needs a child to wipe.',\n})","reach":[{"when":"a straight edge sweeping across","use":"`coverage.directional`"},{"when":"two doors opening from the middle","use":"`coverage.directional` with `.folded()`"},{"when":"venetian blinds or random bars","use":"`coverage.directional` with `.tiled(n)`, or `.bands(n).shuffled()`"},{"when":"a circle growing from a point","use":"`coverage.radial`"},{"when":"rings popping outward","use":"`coverage.radial` with `.bands(n)`"},{"when":"a clock hand sweeping around a point","use":"`coverage.angular`"},{"when":"blocks dissolving, a checkerboard, or a lattice of diamonds","use":"`coverage.cells` with `.shuffled()`, `.checker()` or `.diamond()`"},{"when":"a soft film dissolve","use":"`coverage.noise`"},{"when":"the timing props every wipe shares","use":"`reveal`"}]},"effects.instances":{"id":"effects.instances","title":"Instances","group":"effects","module":"std/effects/instances.ts","access":{"kind":"namespace","name":"effects.instances"},"curated":true,"words":["effects.instances.repeatInstances","effects.instances.grid","effects.instances.radial","effects.instances.line","effects.instances.byMode"],"types":["effects.instances.GridSlots","effects.instances.LineSlots","effects.instances.PlacementPick","effects.instances.PlacementSpec","effects.instances.RadialSlots","effects.instances.RepeatInstancesRecipe","effects.instances.VariationSlots"],"intro":"Repeat the layer inside. `repeatInstances` draws many copies of it, laid out in a grid, around\na circle or along a line, each copy scaled, turned, faded and jittered a little more than the\nlast. It returns the fragment for `gpu.fragment`. The layout words (`grid`, `radial`, `line`)\nsay where the copies go, `byMode` lets one select prop switch between all three, and the\nrecipe's `variation` object says how the copies differ.\n\nThe definition sets `requiresRTT`, `requiresChild` and `wantsBoundsParams`: copies are cut from\nthe layer's own bounds and spread across this layer's bounding box. Radius, spacing and gaps\nare fractions of the canvas, layout angles are in degrees, and the per-copy `rotation` and\n`hueShift` are in radians (give those props a degrees-to-radians transform).","example":"import {defineShader, p, effects, crosses, transformBoolean} from 'shaders/std'\n\nconst {repeatInstances, radial} = effects.instances\n\nconst degToRad = (v: number) => (v * Math.PI) / 180\n\n// Orbit: copies of the layer inside around a circle, each one smaller, fainter and hue-shifted.\nexport const Orbit = defineShader({\n  name: 'Orbit',\n  requiresRTT: true,\n  requiresChild: true,\n  wantsBoundsParams: true,\n  props: {\n    count: {default: 8},\n    radius: {default: 0.3},\n    startAngle: {default: 0},\n    sweep: {default: 360},\n    faceCenter: {default: false, transform: transformBoolean},\n    instanceScale: {default: 0.9},\n    instanceRotation: {default: 0, transform: degToRad},\n    instanceOpacity: {default: 0.9},\n    jitterPosition: {default: 0},\n    jitterRotation: {default: 0},\n    jitterScale: {default: 0},\n    jitterOpacity: {default: 0},\n    seed: {default: 0},\n    phase: {default: 0},\n    hueShift: {default: 30, transform: degToRad, recompile: crosses(0)},\n    zOrder: {default: 'forward', transform: (v: string) => (v === 'backward' ? 1 : 0), compileTime: true},\n    cropLeft: {default: 0},\n    cropRight: {default: 0},\n    cropTop: {default: 0},\n    cropBottom: {default: 0},\n  },\n  gpu: {fragment: repeatInstances({\n    source: {crop: {left: p('cropLeft'), right: p('cropRight'), top: p('cropTop'), bottom: p('cropBottom')}},\n    placement: radial({count: p('count'), radius: p('radius'), startAngle: p('startAngle'), sweep: p('sweep'), faceCenter: p('faceCenter')}),\n    variation: {\n      scale: p('instanceScale'), rotation: p('instanceRotation'), opacity: p('instanceOpacity'),\n      jitter: {position: p('jitterPosition'), rotation: p('jitterRotation'), scale: p('jitterScale'), opacity: p('jitterOpacity')},\n      seed: p('seed'), phase: p('phase'),\n    },\n    hueShift: p('hueShift'),\n    order: p('zOrder'),\n  })},\n})","reach":[{"when":"copies in rows and columns, brick-staggered or mirrored","use":"`grid`"},{"when":"copies around a circle, or along an arc","use":"`radial`"},{"when":"copies trailing off in one direction","use":"`line`"},{"when":"one select prop that switches between layouts","use":"`byMode`"},{"when":"the copies themselves, with their variation","use":"`repeatInstances`"}]},"effects.fracture":{"id":"effects.fracture","title":"Fracture","group":"effects","module":"std/effects/fracture.ts","access":{"kind":"namespace","name":"effects.fracture"},"curated":true,"words":["effects.fracture.voronoiRegionField","effects.fracture.crackGeom","effects.fracture.crackRefractUV","effects.fracture.crackShardCompose","effects.fracture.cellLaneUVFor"],"types":[],"intro":"Broken glass. The canvas splits into **shards** around a set of random sites; a pixel belongs\nto the nearest site. Where a shard has moved, a **crack** opens along its border with the\nnext-nearest shard, and the layer inside bends and colour-splits across it like light through\na prism. `voronoiRegionField` is the compute hook (for `compute:`) that finds each pixel's\nshard and the next-nearest one; it only recomputes when the seed changes. The crack words run\nin the fragment: `crackGeom` measures the crack at a pixel, `crackRefractUV` bends one colour\nchannel across it, and `crackShardCompose` joins the three channels with a little light on\ntilted shards.\n\nEach shard's position and displacement reach the fragment through a one-row data texture you\nfill on the CPU (the Shatter shader moves them with pointer physics every frame). Lay it out as\nfour values per shard and read it with `cellLaneUVFor`. The crack words are GPU functions:\ninvoke one with `call(fn, 'name', [args])`. Samples come from the layer's texture, so\nunpremultiply the final colour.","example":"import {defineShader, effects, math, call} from 'shaders/std'\nimport type {Expr, GpuFragmentParams, KitTexture} from 'shaders/std'\n\nconst {voronoiRegionField, cellLaneUVFor, crackGeom, crackRefractUV, crackShardCompose} = effects.fracture\nconst {vec4, div, max, sub, mul, float} = math\n\nconst SHARDS = 16\nconst cellLaneUV = cellLaneUVFor(SHARDS * 4)\n\n// Deterministic shard sites for a seed, in uv. Shared by the region field and the data texture.\nconst rand = (n: number): number => {\n  const x = Math.sin(n) * 10000\n  return x - Math.floor(x)\n}\nconst sites = (seed: number): Float32Array => {\n  const out = new Float32Array(SHARDS * 2)\n  for (let i = 0; i < SHARDS; i++) {\n    out[i * 2] = rand(seed + i * 2)\n    out[i * 2 + 1] = rand(seed + i * 2 + 1)\n  }\n  return out\n}\n\n// Straight alpha from a premultiplied sample.\nconst unpremultiply = (c: Expr): Expr => vec4(div(c.member('rgb'), max(c.member('a'), 0.0001)), c.member('a'))\n\n// Frozen shatter: every shard sits a little away from the centre, so the cracks stay open.\nexport const FrozenShatter = defineShader({\n  name: 'FrozenShatter',\n  requiresRTT: true,\n  requiresChild: true,\n  props: {\n    crackWidth: {default: 1},\n    spread: {default: 1, compileTime: true},\n    refractionStrength: {default: 5},\n    chromaticSplit: {default: 1},\n    shardLighting: {default: 0.1},\n    seed: {default: 2, compileTime: true},\n  },\n  compute: voronoiRegionField({count: SHARDS, size: 1024, sites, seedProp: 'seed', seedDefault: 2, output: 'shards'}),\n  gpu: {fragment: (params: GpuFragmentParams): Expr => {\n    const {childNode, ctx, uniforms, computeOutputs, convertToTexture, getCpuValue, createDataTexture, registerMediaTexture, onCleanup} = params\n    if (!childNode) return vec4(0, 0, 0, 0)\n    const child = convertToTexture(childNode)\n    const field = computeOutputs?.shards as KitTexture | undefined\n    if (!field) return unpremultiply(child.sample(ctx.uv))\n\n    // One row, four values per shard: x, y, then the displacement encoded as byte = d / 0.1 + 0.5.\n    const seed = (getCpuValue('seed') as number) ?? 2\n    const spread = (getCpuValue('spread') as number) ?? 1\n    const pos = sites(seed)\n    const bytes = new Uint8Array(SHARDS * 4 * 4)\n    for (let i = 0; i < SHARDS; i++) {\n      const dx = (pos[i * 2] - 0.5) * 0.05 * spread\n      const dy = (pos[i * 2 + 1] - 0.5) * 0.05 * spread\n      const lane = [pos[i * 2], pos[i * 2 + 1], dx / 0.1 + 0.5, dy / 0.1 + 0.5]\n      for (let f = 0; f < 4; f++) bytes[(i * 4 + f) * 4] = Math.round(Math.min(1, Math.max(0, lane[f])) * 255)\n    }\n    const data = createDataTexture({width: SHARDS * 4, height: 1, format: 'rgba8unorm', data: bytes})\n    onCleanup(() => data.destroy())\n    const dataKit = registerMediaTexture(() => data.texture)\n    const at = (idx: Expr, f: number): Expr => dataKit.sample(call(cellLaneUV, 'cellLaneUV', [idx, float(f)]), 'nearestClamp').member('r')\n    const dispAt = (idx: Expr, f: number): Expr => mul(sub(at(idx, f), 0.5), 0.1)\n\n    const cell = field.sample(ctx.uv, 'nearestClamp')\n    const nearest = cell.member('r')\n    const second = cell.member('g')\n    const geom = call(crackGeom, 'crackGeom', [\n      ctx.uv, at(nearest, 0), at(nearest, 1), at(second, 0), at(second, 1), dispAt(nearest, 2), dispAt(nearest, 3), uniforms.crackWidth,\n    ])\n    const refracted = (channel: number): Expr => child.sample(call(crackRefractUV, 'crackRefractUV', [\n      geom.member('displacedUV'), geom.member('edgeNormal'), geom.member('crackIntensity'), uniforms.refractionStrength, uniforms.chromaticSplit, float(channel),\n    ]))\n    const glass = call(crackShardCompose, 'crackShardCompose', [\n      child.sample(geom.member('displacedUV')), refracted(1), refracted(0), refracted(-1), geom.member('crackIntensity'), geom.member('disp'), uniforms.shardLighting,\n    ])\n    return unpremultiply(glass)\n  }},\n})","reach":[{"when":"splitting the canvas into shards that follow a seed","use":"`voronoiRegionField`"},{"when":"reading one shard's position or displacement from your data texture","use":"`cellLaneUVFor`"},{"when":"how wide the crack is at a pixel and where the shard has gone","use":"`crackGeom`"},{"when":"bending and colour-splitting the layer across the crack","use":"`crackRefractUV`"},{"when":"the final glass colour with shard lighting","use":"`crackShardCompose`"}]},"effects.edgeGlow":{"id":"effects.edgeGlow","title":"Edge glow","group":"effects","module":"std/effects/edgeGlow.ts","access":{"kind":"namespace","name":"effects.edgeGlow"},"curated":true,"words":["effects.edgeGlow.edgeGlowBand","effects.edgeGlow.orbitSpotsAccum","effects.edgeGlow.heartbeatPulse","effects.edgeGlow.edgeGlowAccumZero","effects.edgeGlow.edgeGlowAccumColorFor","effects.edgeGlow.edgeGlowCompose","effects.edgeGlow.colorAtIndex"],"types":[],"intro":"A band of light hugging the edge of any shape, with coloured spots racing around it. The words\nlive inside a `shapedSurface` surface function (from `materials`), which hands them the\nshape's **distance field**: for every pixel, how far it is from the shape's edge, negative\ninside. `edgeGlowBand` turns that distance into the glowing band (with drifting smoke) and\nalso gives the angle around the shape. `orbitSpotsAccum` places one colour's spots along that\nangle. The spots of every colour are folded together, starting from `edgeGlowAccumZero` and\nadding one colour at a time with the step from `edgeGlowAccumColorFor`, and `edgeGlowCompose`\nturns the fold into the final colour with bloom. `heartbeatPulse` is the beat the band and the\nspots share.\n\nThese are GPU functions: invoke one with `call(fn, 'name', [args])`. The result is straight\nalpha over a transparent background, so it composites over whatever sits behind the shape.","example":"import {defineShader, effects, materials, math, call, animatedTime, transformPosition, transformColor, transformColorSpace} from 'shaders/std'\nimport type {Expr} from 'shaders/std'\n\nconst {heartbeatPulse, edgeGlowBand, orbitSpotsAccum, edgeGlowAccumZero, edgeGlowAccumColorFor, edgeGlowCompose} = effects.edgeGlow\n\n// Halo: two colours of light chasing each other around the edge of a shape, with smoke and bloom.\nexport const Halo = defineShader({\n  name: 'Halo',\n  animatedTime: {speed: 'speed'},\n  props: {\n    center: {default: {x: 0.5, y: 0.5}, transform: transformPosition},\n    scale: {default: 1},\n    rotation: {default: 0},\n    shape: {default: JSON.stringify({type: 'circleSDF', radius: 0.35}), ui: {type: 'shape', label: 'Shape', group: 'Shape'}},\n    shapeSdfUrl: {default: '', compileTime: true},\n    shapeType: {default: '', compileTime: true},\n    colorA: {default: '#7b2ff7', transform: transformColor},\n    colorB: {default: '#00e0ff', transform: transformColor},\n    colorSpace: {default: 'oklch', transform: transformColorSpace, compileTime: true},\n    thickness: {default: 0.25},\n    softness: {default: 0.5},\n    intensity: {default: 0.6},\n    bloom: {default: 0.3},\n    spots: {default: 3},\n    spotSize: {default: 0.5},\n    pulse: {default: 0},\n    smoke: {default: 0.3},\n    smokeSize: {default: 0.5},\n    speed: {default: 1},\n    seed: {default: 0},\n  },\n  ...materials.shapedSurface({\n    chord: 'firstLobe',\n    stencil: 'centre',\n    surface: (frame, params) => {\n      const {uniforms} = params\n      const t = animatedTime(params)\n      // One beat per pixel, shared by the band's smoke and every spot orbit.\n      const beat = math.local(call(heartbeatPulse, 'heartbeatPulse', [t]), 'beat')\n      const band = math.local(call(edgeGlowBand, 'edgeGlowBand', [\n        frame.surf0!.member('r'), frame.sdfUV!, params.ctx.viewportSize, uniforms.scale, uniforms.thickness, uniforms.softness,\n        uniforms.smokeSize, uniforms.smoke, t, uniforms.pulse, beat,\n      ]), 'band')\n      const spotsFor = (colorIndex: number): Expr => call(orbitSpotsAccum, 'orbitSpotsAccum', [\n        band.member('y'), band.member('x'), t, beat, uniforms.spots, uniforms.spotSize, uniforms.pulse,\n        uniforms.intensity, uniforms.softness, uniforms.seed, math.float(colorIndex),\n      ])\n      // Fold the two colours together in the chosen colour space, then compose with bloom.\n      const accumColor = edgeGlowAccumColorFor((params.propValues.colorSpace as number) ?? 0)\n      let state = call(edgeGlowAccumZero, 'edgeGlowAccumZero', [])\n      state = call(accumColor, 'edgeGlowAccumColor', [state, uniforms.colorA, spotsFor(0)])\n      state = call(accumColor, 'edgeGlowAccumColor', [state, uniforms.colorB, spotsFor(1)])\n      return call(edgeGlowCompose, 'edgeGlowCompose', [state, uniforms.bloom])\n    },\n  }),\n})","reach":[{"when":"a glowing outline around a 2D, SVG or 3D shape","use":"`edgeGlowBand`"},{"when":"coloured lights chasing each other around that outline","use":"`orbitSpotsAccum`"},{"when":"several colours of light that overlap without going muddy","use":"`edgeGlowAccumColorFor` with `edgeGlowAccumZero`"},{"when":"the colours from a `stops` prop, one spot orbit each","use":"`colorAtIndex`"},{"when":"a pulse the band and the spots beat to","use":"`heartbeatPulse`"},{"when":"the final colour, with bloom","use":"`edgeGlowCompose`"}]},"effects.overlay":{"id":"effects.overlay","title":"Overlays","group":"effects","module":"std/effects/overlay.ts","access":{"kind":"namespace","name":"effects.overlay"},"curated":true,"words":["effects.overlay.detectionOverlay","effects.overlay.motionTrackerHud"],"types":["effects.overlay.DetectionOverlaySlots","effects.overlay.MotionTrackerHudSlots"],"intro":"Heads-up-display overlays: whole fragments that look at the layer inside, find things in it\nand draw chrome on top. `detectionOverlay` cuts the canvas into cells, finds the bright,\ndark, colored or opaque content in each and draws a bounding box with an optional label\naround it, the computer-vision look. `motionTrackerHud` draws tracker gizmos that chase\ncontent over time, dropping keyframe diamonds behind them along their paths.\n\nBoth words return a fragment for a `gpu:` definition rather than an `effect:`; the child is\nread as a texture and shows through underneath. `motionTrackerHud` only draws: the trackers\ncome from the tracker pursuit simulation, which the same definition runs as its `compute:`\npass. Sizes are in pixels on a 1080-pixel-tall canvas and scale with the canvas height.\nColors are color props.","example":"import {defineShader, p, effects, transformColor, transformBoolean} from 'shaders/std'\n\nconst {detectionOverlay} = effects.overlay\n\n// Object detection HUD: corner-bracket boxes around bright content, with a dimensions label.\nexport const Detector = defineShader({\n  name: 'Detector',\n  role: 'overlay',\n  requiresRTT: true,\n  requiresChild: true,\n  props: {\n    detectionMode: {default: 'bright', compileTime: true, ui: {type: 'select', options: [{label: 'Bright', value: 'bright'}, {label: 'Dark', value: 'dark'}, {label: 'Opaque', value: 'alpha'}]}},\n    threshold: {default: 0.25, ui: {type: 'range', min: 0, max: 1, step: 0.01}},\n    layout: {default: 'mosaic', compileTime: true, ui: {type: 'select', options: [{label: 'Grid', value: 'grid'}, {label: 'Quadtree', value: 'quadtree'}, {label: 'Mosaic', value: 'mosaic'}]}},\n    cellSize: {default: 400, ui: {type: 'range', min: 24, max: 800, step: 1}},\n    maxDepth: {default: 2, compileTime: true, ui: {type: 'range', min: 1, max: 5, step: 1}},\n    boxStyle: {default: 'corners', compileTime: true, ui: {type: 'select', options: [{label: 'Full', value: 'full'}, {label: 'Corners', value: 'corners'}]}},\n    lineWidth: {default: 1.5, ui: {type: 'range', min: 0.5, max: 12, step: 0.5}},\n    cornerRadius: {default: 0, ui: {type: 'range', min: 0, max: 40, step: 1}},\n    strokeColor: {default: '#ffffff', transform: transformColor},\n    fillColor: {default: '#5a79911a', transform: transformColor},\n    labelColor: {default: '#000000', transform: transformColor},\n    labelBackgroundColor: {default: '#ffffff', transform: transformColor},\n    labelMode: {default: 'dimensions', compileTime: true, ui: {type: 'select', options: [{label: 'None', value: 'none'}, {label: 'Dimensions', value: 'dimensions'}, {label: 'Percentage', value: 'percentage'}]}},\n    labelPosition: {default: 'bottom-right', compileTime: true, ui: {type: 'select', options: [{label: 'Bottom left', value: 'bottom-left'}, {label: 'Bottom right', value: 'bottom-right'}, {label: 'Top left', value: 'top-left'}, {label: 'Top right', value: 'top-right'}]}},\n    labelInset: {default: true, transform: transformBoolean, ui: {type: 'checkbox'}},\n    labelRadius: {default: 0, ui: {type: 'range', min: 0, max: 30, step: 1}},\n    fontFamily: {default: 'Inter', ui: {type: 'font-family'}},\n    fontWeight: {default: 500, ui: {type: 'font-weight'}},\n    fontSize: {default: 0.015, ui: {type: 'range', min: 0.01, max: 0.5, step: 0.005}},\n    letterSpacing: {default: 0, ui: {type: 'range', min: -0.1, max: 0.5, step: 0.005}},\n  },\n  gpu: {fragment: detectionOverlay({\n    detectionMode: p('detectionMode'), threshold: p('threshold'),\n    layout: p('layout'), cellSize: p('cellSize'), maxDepth: p('maxDepth'),\n    boxStyle: p('boxStyle'), lineWidth: p('lineWidth'), cornerRadius: p('cornerRadius'),\n    strokeColor: p('strokeColor'), fillColor: p('fillColor'),\n    labelColor: p('labelColor'), labelBackgroundColor: p('labelBackgroundColor'),\n    labelMode: p('labelMode'), labelPosition: p('labelPosition'),\n    labelInset: p('labelInset'), labelRadius: p('labelRadius'),\n    fontFamily: p('fontFamily'), fontWeight: p('fontWeight'),\n    fontSize: p('fontSize'), letterSpacing: p('letterSpacing'),\n  })},\n})","reach":[{"when":"bounding boxes and labels around whatever is in the layer","use":"`detectionOverlay`"},{"when":"trackers that follow moving content and leave keyframe trails","use":"`motionTrackerHud`"}]},"effects.pointerFields":{"id":"effects.pointerFields","title":"Pointer fields","group":"effects","module":"std/effects/pointerFields.ts","access":{"kind":"namespace","name":"effects.pointerFields"},"curated":true,"words":["effects.pointerFields.pointerSplatField","effects.pointerFields.springLatticeField","effects.pointerFields.clampSplatGridSize","effects.pointerFields.SPLAT_MIN_GRID","effects.pointerFields.SPLAT_MAX_GRID"],"types":[],"intro":"Push fields the pointer stirs. Each word is a whole simulation: a grid of cells, each holding\nhow far the layer inside should shift there, that the cursor pushes and that settles back on\nits own. Spread the word into a warp definition with `...sim.grids.gridSim(word)` and it\npublishes the field under its `output` name; the warp's `map:` then reads it with\n`warps.gridCellDisplace` (per cell, blocky) or `warps.liquidDisplace` (smoothly, scaled by an\nintensity prop). The definition needs `usesPointer: true`.\n\n`pointerSplatField` smears pixels along the cursor and lets them fade back; its grid size is a\nprop, so pair it with `clampSplatGridSize` in a recompile rule. `springLatticeField` is a cloth\nof springs on a fixed 64-cell grid that stretches, rings and settles like fabric. Both stop\ncomputing once the field has settled, and both give a displacement in uv.","example":"import {defineShader, p, effects, sim, warps, recompileWhen, transformEdges} from 'shaders/std'\n\nconst {pointerSplatField, clampSplatGridSize} = effects.pointerFields\n\n// Smear: the layer inside follows the cursor in coarse cells and eases back when it stops.\nexport const Smear = defineShader({\n  name: 'Smear',\n  usesPointer: true,\n  props: {\n    intensity: {default: 1},\n    decay: {default: 3},\n    radius: {default: 1},\n    // Rebuild the simulation only when the cell count really changes, not on every slider step.\n    gridSize: {default: 20, recompile: recompileWhen((prev, next) => clampSplatGridSize(prev as number) !== clampSplatGridSize(next as number))},\n    edges: {default: 'stretch', transform: transformEdges, compileTime: true},\n  },\n  ...sim.grids.gridSim(pointerSplatField({\n    gridSize: p('gridSize'), decay: p('decay'), intensity: p('intensity'), radius: p('radius'),\n    output: 'displacement',\n  })),\n  map: warps.gridCellDisplace({gridSize: p('gridSize'), output: 'displacement'}),\n  // Before the field exists (no GPU yet), the warp passes the layer through untouched.\n  uvRemapIdentityWhen: ({computeOutputs}) => !computeOutputs?.displacement,\n})","reach":[{"when":"a smear that follows the cursor and fades","use":"`pointerSplatField`"},{"when":"fabric or liquid that ripples after a push","use":"`springLatticeField`"},{"when":"the grid-size prop's recompile rule","use":"`clampSplatGridSize`"},{"when":"the range that rule holds the grid to","use":"`SPLAT_MIN_GRID`, `SPLAT_MAX_GRID`"}]},"simulate":{"id":"simulate","title":"Simulate","group":"simulation","module":"std/sim.ts","access":{"kind":"top-level"},"curated":true,"words":["simulate.simulate","simulate.simulate.grid","simulate.op","simulate.op.wave","simulate.op.splat","simulate.op.gradient","simulate.GridSim"],"types":["simulate.GradientOp","simulate.GridDeriveOp","simulate.GridSimConfig","simulate.GridStepOp","simulate.SimOutputRef","simulate.SplatOp","simulate.WaveOp"],"intro":"Say \"simulate this\" and let the engine run it. `simulate.grid` declares a square grid of\nnumbers that changes a little every frame: you list the steps that advance it (`op.wave`\nmakes it ripple, `op.splat` presses the pointer into it) and name the outputs you want derived\nfrom it (`op.gradient`, the slope at every cell). The engine allocates the state, keeps the\nhistory a wave needs, tracks the pointer, clamps the frame clock, and stops dispatching once\nthe field has faded to nothing. There is no GPU code in the definition at all.\n\nAn **output** is a field an effect can read. Declare the simulation once at module scope, then\nhand `waves.output('displacement')` to `displaceBy`, which bends the layer inside along the\nslope with a chromatic split and edge handling. Today the engine implements exactly one recipe,\n`[op.wave, op.splat]` with a derived `op.gradient`; a different configuration throws when the\nshader is defined, never at render time.\n\nWhen an effect needs its own GPU step (a reaction-diffusion rule, a fluid, a flock), the\nlower-level families take over: `sim.grids`, `sim.fluids`, `sim.feedback`, `sim.agents`.","example":"import {defineShader, p, simulate, op, pointer, pointerSpeed, displaceBy, transformEdges} from 'shaders/std'\n\n// A 128-cell wave field the pointer stirs. The engine keeps two frames of history for the wave,\n// tracks the pointer (a jump into the canvas is not a stroke), and sleeps once the field has decayed.\nconst waves = simulate.grid({\n  resolution: 128,\n  history: 2,\n  step: [\n    op.wave({damping: p('decay')}),\n    op.splat({at: pointer({teleportGuard: 'on'}), amount: pointerSpeed({max: 2}), radius: p('radius')}),\n  ],\n  derive: {displacement: op.gradient()},\n  rest: {settlesWhen: 'derived-from-damping'},\n})\n\n// Ripples: the layer inside is displaced along the wave's slope, with a chromatic split at the crests.\nexport const Ripples = defineShader({\n  name: 'Ripples',\n  props: {\n    intensity: {default: 10},\n    decay: {default: 10},\n    radius: {default: 0.5},\n    chromaticSplit: {default: 1},\n    edges: {default: 'stretch', transform: transformEdges, compileTime: true},\n  },\n  effect: displaceBy(waves.output('displacement'), {\n    strength: p('intensity'),\n    chromatic: p('chromaticSplit'),\n    edges: p('edges'),\n  }),\n})","reach":[{"when":"ripples that spread from the cursor and distort the layer inside","use":"`simulate.grid` with `op.wave`, `op.splat`, `op.gradient`, read by `displaceBy`"},{"when":"the ripples should fade faster or ring longer","use":"`op.wave` with a damping prop, 0 rings forever, 20 dies in frames"},{"when":"the brush should be bigger or press harder","use":"`op.splat` with a radius prop and `pointerSpeed`"},{"when":"the simulation should sleep once still","use":"the rest option on `simulate.grid`"},{"when":"a handle to the field for an effect","use":"`GridSim` and its output method"}]},"sim.grids":{"id":"sim.grids","title":"Grid programs","group":"simulation","module":"std/sim/grids.ts","access":{"kind":"namespace","name":"sim.grids"},"curated":true,"words":["sim.grids.gridSim","sim.grids.op","sim.grids.op.values","sim.grids.op.pass","sim.grids.op.publish","sim.grids.op.iterate","sim.grids.op.seedOnce","sim.grids.op.sortPass","sim.grids.op.cache","sim.grids.op.readyWhen","sim.grids.op.settle","sim.grids.op.host","sim.grids.trackedViewport","sim.grids.hostGridProgram","sim.grids.hostStep","sim.grids.hostFieldTexture"],"types":["sim.grids.GridFrame","sim.grids.GridFrameParams","sim.grids.GridRoot","sim.grids.GridSimConfig","sim.grids.GridStage","sim.grids.HostFrame","sim.grids.HostStep","sim.grids.SortPassStage"],"intro":"A grid simulation is a fixed-size field of cells (a displacement per cell, two chemical\nconcentrations, a sort offset) that changes a little every frame. `gridSim` runs it: you\ndescribe the frame as an ordered list of **stages** built with `op.*`, and the engine runs that\nlist every frame, owning the clock, the frame-delta clamp, the ready gate, the settle gate and\nthe outputs the layer samples. A stage is bookkeeping (`op.host`, `op.values`, `op.readyWhen`,\n`op.settle`) or work (`op.pass`, `op.cache`, `op.seedOnce`, `op.iterate`, `op.sortPass`), and\nevery frame ends with `op.publish`, which writes the state into the texture the fragment reads.\n\nThe build callback runs once per instance of the layer and returns the outputs, the clamp and\nthe stages; anything it creates (buffers, passes, trackers) belongs to that instance. A settled\nfield costs nothing: once `op.settle` sees nothing driving it, frames are skipped and the last\npublished picture stays on screen.\n\nTwo shortcuts sit on top. The pointer fields in `effects.pointerFields` are whole grid programs\nyou spread as `...gridSim(springLatticeField({...}))` and read with a warp map. And for a small\nfield whose arithmetic reads better as JavaScript loops, `hostGridProgram` runs the same named\nsteps on the CPU and `hostFieldTexture` uploads the cells as a texture. When a wave field and a\ndisplacement are all you need, declare them with `simulate.grid` instead.","example":"import {defineShader, p, sim, effects, warps, transformEdges} from 'shaders/std'\n\nconst {gridSim} = sim.grids\nconst {springLatticeField} = effects.pointerFields\n\n// Liquify: a spring lattice the cursor pushes, published as a displacement field the warp map reads.\n// The pointer field packages the whole stage list (pointer tracking, values, the step passes, the\n// settle gate, publish); this definition only names the props and the output.\nexport const Liquify = defineShader({\n  name: 'Liquify',\n  usesPointer: true,\n  props: {\n    intensity: {default: 10},\n    stiffness: {default: 3},\n    damping: {default: 3},\n    radius: {default: 1},\n    edges: {default: 'stretch', transform: transformEdges, compileTime: true},\n  },\n  ...gridSim(springLatticeField({\n    stiffness: p('stiffness'), damping: p('damping'), radius: p('radius'),\n    output: 'displacement',\n  })),\n  // Bend the incoming coordinate by the field, scaled by intensity, then handle the edges.\n  map: warps.liquidDisplace({intensity: p('intensity'), output: 'displacement'}),\n  // Before the field exists (no GPU yet), the warp passes the layer through untouched.\n  uvRemapIdentityWhen: ({computeOutputs}) => !computeOutputs?.displacement,\n})","reach":[{"when":"liquid or fabric that ripples after the cursor pushes it","use":"`gridSim` with `springLatticeField` and a warp map"},{"when":"a rule that must run N times per frame (reaction-diffusion)","use":"`op.iterate` after `op.values`, then `op.publish`"},{"when":"a sort that settles a little more every frame","use":"`op.sortPass`, with `op.publish` reading its side"},{"when":"the state must be reset on the first frame or a seed change","use":"`op.seedOnce`"},{"when":"a pass that needs the child's picture, which arrives late","use":"`op.readyWhen` and `op.cache`"},{"when":"the simulation should sleep once nothing drives it","use":"`op.settle`"},{"when":"this frame's values written before the passes read them","use":"`op.values`"},{"when":"the canvas size or aspect inside a stage","use":"`trackedViewport`"},{"when":"a small field stepped in plain JavaScript","use":"`hostGridProgram` with `hostStep`, published by `hostFieldTexture`"}]},"sim.fluids":{"id":"sim.fluids","title":"Fluids","group":"simulation","module":"std/sim/fluids.ts","access":{"kind":"namespace","name":"sim.fluids"},"curated":true,"words":["sim.fluids.fluidSim","sim.fluids.splat","sim.fluids.cursorRibbon","sim.fluids.seededFieldInit","sim.fluids.ambientForce","sim.fluids.restoreToward"],"types":["sim.fluids.CursorRibbonSpec","sim.fluids.FluidContainerParts","sim.fluids.FluidEmitter","sim.fluids.FluidFrame","sim.fluids.FluidFrameParams","sim.fluids.FluidInitIo","sim.fluids.FluidInitPart","sim.fluids.FluidKernel","sim.fluids.FluidRoot","sim.fluids.FluidSetupCtx","sim.fluids.FluidSimConfig","sim.fluids.FluidSolveStage"],"intro":"Smoke, ink and fog that flow. A fluid is a square grid of velocity plus a **dye** (a density\nand an age, or a color) that the engine advances with a full fluid solve every frame:\nswirl, pressure, advection. `fluidSim` owns the state buffers, the output texture, the solve\nchain, the pointer tracker, the clock and the idle gate that sleeps a settled field. You bring\nthe parts that make the look and declare them by role.\n\n**Emitters** put dye and momentum in: `splat` at a fixed source (a cone from a point),\n`cursorRibbon` along the pointer's stroke as a continuous ribbon of stamps. **Solve stages**\nwrap every solve: `ambientForce` before it (a turbulence field), `restoreToward` after it (a\ncolor the solve would otherwise blur away). **Init** decides how the field starts:\n`seededFieldInit` seeds it and runs silent warm-up solves so the first visible frame already\nflows. The `values` function turns props, the clock and the pointer into the numbers every pass\nreads; positions and radii in it are in grid cells.\n\nThe fragment samples the output texture under the key you named and colors it however you\nlike. When an effect only needs a wave field or a displacement, `simulate.grid` says it with no\nGPU code.","example":"import {defineShader, p, sim, transformColor, transformPosition} from 'shaders/std'\nimport {tgpu, d} from '@coreroot/gpu/kit'\nimport {buildStableFluidsKernels, buildEmitterSplatKernel, buildFluidOutputKernel, mixExpr, vec4, ZERO} from '@coreroot/gpu/porters'\nimport type {GpuFragmentParams, KitTexture, Expr} from '@coreroot/gpu/porters'\n\nconst {fluidSim, splat} = sim.fluids\n\nconst N = 256\nconst COUNT = N * N\n\n// The values every pass reads each frame. dt, curlStrength, velFade, dyeFade and colorDecay are the\n// solver's; the rest drive the emitter and the cursor shove.\nconst FluidParams = d.struct({\n  dt: d.f32, emitX: d.f32, emitY: d.f32, emitVelX: d.f32, emitVelY: d.f32,\n  perpDirX: d.f32, perpDirY: d.f32, spreadFactor: d.f32, emitRad: d.f32, emitIntensity: d.f32,\n  dyeFade: d.f32, velFade: d.f32, curlStrength: d.f32, gravity: d.f32, cursorX: d.f32, cursorY: d.f32,\n  cursorVelX: d.f32, cursorVelY: d.f32, mouseActive: d.f32, mouseRadSq: d.f32, colorDecay: d.f32,\n})\n// The fluid's buffers, by the names the solver expects.\nconst fluidLayout = tgpu.bindGroupLayout({\n  velA: {storage: d.arrayOf(d.vec4f, COUNT), access: 'mutable'},\n  velB: {storage: d.arrayOf(d.vec4f, COUNT), access: 'mutable'},\n  dyeA: {storage: d.arrayOf(d.vec4f, COUNT), access: 'mutable'},\n  dyeB: {storage: d.arrayOf(d.vec4f, COUNT), access: 'mutable'},\n  pressure: {storage: d.arrayOf(d.f32, COUNT), access: 'mutable'},\n  divergence: {storage: d.arrayOf(d.f32, COUNT), access: 'mutable'},\n  params: {uniform: FluidParams},\n})\nconst outputLayout = tgpu.bindGroupLayout({\n  dyeA: {storage: d.arrayOf(d.vec4f, COUNT), access: 'readonly'},\n  outTex: {storageTexture: d.textureStorage2d('rgba16float', 'write-only')},\n})\n\n// The one pass that is this shader's own: a cone emitter at a fixed source, plus the cursor shove.\nconst splatKernel = buildEmitterSplatKernel(fluidLayout, {n: N, namePrefix: 'smoke', cone: true, cursorPush: true, densityGain: 8, velocityBlendGain: 3})\n// Everything else is the shared solve over a density-and-age dye.\nconst solverKernels = buildStableFluidsKernels(fluidLayout, {n: N, namePrefix: 'smoke', boundary: 'clamped', dye: 'densityAge', dyeDissipation: true, ageAdvance: true})\nconst outputKernel = buildFluidOutputKernel(outputLayout, {n: N, namePrefix: 'smoke', dye: 'densityAge'})\n\n// Smoke: fresh smoke in colorA ages toward colorB as it rises and dissipates.\nexport const Smoke = defineShader({\n  name: 'Smoke',\n  usesPointer: true,\n  props: {\n    colorA: {default: '#fc83f9', transform: transformColor},\n    colorB: {default: '#c21c79', transform: transformColor},\n    emitFrom: {default: {x: 0.5, y: 1}, transform: transformPosition},\n    speed: {default: 20},\n    emitRadius: {default: 0.08},\n    dissipation: {default: 0.2},\n    detail: {default: 25},\n    mouseInfluence: {default: 0.1},\n  },\n  ...fluidSim(() => ({\n    resolution: N,\n    layout: fluidLayout, outputLayout, paramsSchema: FluidParams,\n    solver: {kernels: solverKernels, jacobiIters: 10},\n    output: {kernel: outputKernel, key: 'smokeTexture'},\n    pointer: {minDrag: 0.0005},\n    inject: [splat({kernel: splatKernel})],\n    values: (f) => {\n      const emit = f.getCpuValue('emitFrom') as {x: number; y: number} | undefined\n      const velMag = f.num('speed', 20) * N * 0.15\n      const ptr = f.ptr!\n      const mouseInf = f.num('mouseInfluence', 0.1)\n      const active = ptr.moving && mouseInf > 0\n      const mouseRadGrid = 0.1 * N\n      return {\n        dt: f.dt,\n        emitX: (emit?.x ?? 0.5) * N,\n        emitY: (1 - (emit?.y ?? 0)) * N,\n        emitVelX: 0, emitVelY: -velMag,\n        perpDirX: 1, perpDirY: 0,\n        spreadFactor: Math.tan(Math.PI / 6),\n        emitRad: f.num('emitRadius', 0.08) * N,\n        emitIntensity: 1,\n        dyeFade: f.num('dissipation', 0.2),\n        velFade: 0.2,\n        curlStrength: f.num('detail', 25),\n        gravity: 0.5,\n        colorDecay: 0.4,\n        cursorX: active ? ptr.x * N : 0,\n        cursorY: active ? ptr.y * N : 0,\n        cursorVelX: active ? ptr.dx * N * 15 * mouseInf : 0,\n        cursorVelY: active ? ptr.dy * N * 15 * mouseInf : 0,\n        mouseActive: active ? 1 : 0,\n        mouseRadSq: mouseRadGrid * mouseRadGrid,\n      }\n    },\n  })),\n  gpu: {\n    fragment: ({ctx, computeOutputs, uniforms}: GpuFragmentParams): Expr => {\n      const field = computeOutputs?.smokeTexture as KitTexture | undefined\n      if (!field) return ZERO // no GPU: transparent\n      const s = field.sample(ctx.uv, 'linearClamp') // x = density, y = age\n      const color = mixExpr(uniforms.colorA, uniforms.colorB, s.member('y'))\n      return vec4(color.member('rgb'), color.member('a').mul(s.member('x')))\n    },\n  },\n})","reach":[{"when":"smoke rising from a point, stirred by the cursor","use":"`fluidSim` with `splat`"},{"when":"smoke or ink painted along the pointer's stroke","use":"`fluidSim` with `cursorRibbon`"},{"when":"fog that is already everywhere when the layer appears","use":"`seededFieldInit`"},{"when":"a wind or turbulence over the whole field","use":"`ambientForce`"},{"when":"a color that must not wash out over time","use":"`restoreToward`"},{"when":"the simulation should sleep once the last stroke has faded","use":"the fadeSeconds option of `cursorRibbon`"}]},"sim.feedback":{"id":"sim.feedback","title":"Feedback","group":"simulation","module":"std/sim/feedback.ts","access":{"kind":"namespace","name":"sim.feedback"},"curated":true,"words":["sim.feedback.feedbackSim"],"types":["sim.feedback.FeedbackBindContext","sim.feedback.FeedbackKernel","sim.feedback.FeedbackRoot","sim.feedback.FeedbackSimConfig"],"intro":"Effects that remember the last frame. A feedback simulation keeps a picture of its own\nprevious output and advances it by one step of your rule every frame: read last frame's state,\napply the rule, write the new state. Temporal echoes, codec smears and decaying trails all live\nhere. `feedbackSim` keeps the two state textures and swaps them, keeps a display copy the layer\nsamples, binds the child's picture once it exists, and runs a clock you can scale with a speed\nprop.\n\nYou bring one **step** (a GPU function run once per state cell that reads the previous state\nand writes the next) and the `values` it reads each frame. The build callback runs once per\ninstance of the layer; pick the step's variant there when a compile-time prop changes it. The\nfragment samples `display` and mixes it with the live child however the effect wants. Without a\nGPU nothing runs, so the fragment should fall back to the child.\n\nThe simulation needs a child. For a wave field or a displacement, `simulate.grid` says it\nwithout GPU code; for a rule that runs many iterations per frame, see `sim.grids`.","example":"import {defineShader, sim} from 'shaders/std'\nimport {buildMacroblockAdvectKernel, call, mixExpr, ZERO} from '@coreroot/gpu/porters'\nimport type {GpuFragmentParams, KitTexture, Expr} from '@coreroot/gpu/porters'\nimport {blend} from '@coreroot/gpu/kit'\n\nconst {feedbackSim} = sim.feedback\n\nconst STATE_RES = 768\n\n// The rule: each block of the picture either refreshes from the live child or holds and drags\n// its previous contents along a motion vector, like a video stream that lost its keyframes.\nconst decoder = buildMacroblockAdvectKernel({\n  res: STATE_RES,\n  format: 'rgba16float',\n  namePrefix: 'dataMosh',\n  blockMix: {coarseScale: 2.6, coarsePick: 0.35},\n  generationLoss: {levels: 18, amount: 0.12, decay: 0.9985},\n})\n\n// DataMosh: corrupted-codec smearing of the layer inside.\nexport const DataMosh = defineShader({\n  name: 'DataMosh',\n  requiresRTT: true,\n  requiresChild: true,\n  props: {\n    intensity: {default: 0.7},\n    blockSize: {default: 48},\n    drift: {default: 0.35},\n    churn: {default: 0.4},\n    blend: {default: 1},\n    speed: {default: 1},\n    seed: {default: 0},\n  },\n  ...feedbackSim((params, root) => ({\n    size: STATE_RES,\n    format: 'rgba16float',\n    speedProp: 'speed',\n    paramsSchema: decoder.Params,\n    // Hand the step the child, last frame's state, this frame's state, the display copy and the values.\n    bindGroups: ({childTexture, display, paramsBuffer}, read, write) => root.createBindGroup(decoder.layout, {\n      src: childTexture, prev: read.state, next: write.state, display, params: paramsBuffer,\n    } as never),\n    step: decoder.kernel,\n    // The simulation's own clock stops with speed 0, so the smear freezes in place.\n    values: ({dt, localTime}) => ({\n      time: localTime,\n      dt,\n      seed: (params.getCpuValue('seed') as number) ?? 0,\n      intensity: (params.getCpuValue('intensity') as number) ?? 0.7,\n      blockSize: (params.getCpuValue('blockSize') as number) ?? 48,\n      drift: (params.getCpuValue('drift') as number) ?? 0.35,\n      churn: (params.getCpuValue('churn') as number) ?? 0.4,\n    }),\n  })),\n  gpu: {\n    fragment: ({childNode, ctx, computeOutputs, uniforms, convertToTexture}: GpuFragmentParams): Expr => {\n      if (!childNode) return ZERO\n      const display = computeOutputs?.display as KitTexture | undefined\n      const childTex = (computeOutputs?.childTexture as KitTexture | undefined) ?? convertToTexture(childNode)\n      const live = childTex.sample(ctx.uv)\n      if (!display) return call(blend.unpremultiplyAlpha, 'unpremultiplyAlpha', [live]) // no GPU: the child untouched\n      const moshed = display.sample(ctx.uv, 'linearClamp')\n      return call(blend.unpremultiplyAlpha, 'unpremultiplyAlpha', [mixExpr(live, moshed, uniforms.blend)])\n    },\n  },\n})","reach":[{"when":"a codec smear, a temporal echo, a decaying trail of the layer inside","use":"`feedbackSim`"},{"when":"the effect should pause or run faster from a prop","use":"the speedProp option of `feedbackSim`"},{"when":"a second texture that must swap with the state","use":"the extraSlots option of `feedbackSim`"},{"when":"skip the whole step while a prop is 0","use":"the skip option of `feedbackSim`"}]},"sim.agents":{"id":"sim.agents","title":"Agents","group":"simulation","module":"std/sim/agents.ts","access":{"kind":"namespace","name":"sim.agents"},"curated":true,"words":["sim.agents.agentSim","sim.agents.renderAgents","sim.agents.renderAgents.orientedWorld","sim.agents.renderAgents.pointWorld","sim.agents.renderAgents.volume","sim.agents.renderAgents.relief"],"types":["sim.agents.AgentSimBaked","sim.agents.AgentSimConfig","sim.agents.AgentSimFragmentConfig","sim.agents.AgentSimSpread","sim.agents.RenderAgentsPipelines"],"intro":"Thousands of small things that move: flocks, motes, filings, swarms, particles standing on an\nimage. An agent simulation keeps a state per agent, moves every agent one step per frame, draws\neach one into a shared canvas, and hands the result to the layer. `agentSim` owns the plumbing:\nthe state buffers, the output texture, the spawn on the first frame and again on a seed change,\nthe lower count on phones, the child's picture when one is needed, and the fragment that\nsamples the picture over the full canvas.\n\nYou declare three things. The **physics**: a list of `force` or `torque` parts folded into one\nupdate step by an `integrator` (see `sim.agentForces`). The **look**: one `renderAgents`\nvariant that pairs a splat (each agent stamps its shape) with its resolve (the canvas becomes\nthe picture and is cleared for the next frame). The **frame**: a callback that turns props and\nthe pointer into the values the steps read, with `agentFrame` helpers for the arithmetic, and\nreturns `sys.frame({count})`. `bake` runs once per composition with the compile-time props\n(shape, color space) and assembles the steps and their order.\n\nAgents live in canvas units: x from 0 to the aspect ratio, y from 0 to 1, so one unit is the\ncanvas height and distances are the same in every direction. This module re-exports the whole\nagent vocabulary, so one import covers forces, render parts, shape fields and frame helpers.","example":"import {defineShader, sim, transformColor, transformColorSpace, colorSpaceOptions} from 'shaders/std'\nimport {tgpu, d, agents} from '@coreroot/gpu/kit'\nimport {makeCpuValueGetter, readAgentFrame, resolveRenderRes, SIZE_REF_RES} from '@coreroot/gpu/porters'\n\nconst {agentSim, renderAgents, force, integrator} = sim.agents\n\nconst MAX_AGENTS = 4096\nconst RES = resolveRenderRes({desktop: 1024, mobile: 640})\n\n// The values every step reads each frame, written from the props in `frame`.\nconst SimParams = d.struct({\n  colA: d.vec4f, colB: d.vec4f,\n  count: d.f32, trails: d.f32,\n  dt: d.f32, aspect: d.f32, domainX: d.f32,\n  maxSpeed: d.f32, maxForce: d.f32,\n  perceptionSq: d.f32, sepRadiusSq: d.f32,\n  sepW: d.f32, aliW: d.f32, cohW: d.f32,\n  cursorX: d.f32, cursorY: d.f32, cursorMode: d.f32, cursorRadius: d.f32, cursorRadiusSq: d.f32, cursorForce: d.f32,\n  bodyR: d.f32,\n  margin: d.f32, turnForce: d.f32,\n  seed: d.f32,\n})\n// Per-agent state (position + velocity), an excitement value, the two canvases, a trail canvas, the output.\nconst simLayout = tgpu.bindGroupLayout({\n  agents: {storage: d.arrayOf(d.vec4f, MAX_AGENTS), access: 'mutable'},\n  agit: {storage: d.arrayOf(d.f32, MAX_AGENTS), access: 'mutable'},\n  accumE: {storage: d.arrayOf(d.atomic(d.u32), RES * RES), access: 'mutable'},\n  accumS: {storage: d.arrayOf(d.atomic(d.u32), RES * RES), access: 'mutable'},\n  trailBuf: {storage: d.arrayOf(d.vec4f, RES * RES), access: 'mutable'},\n  params: {uniform: SimParams},\n  outTex: {storageTexture: d.textureStorage2d('rgba16float', 'write-only')},\n})\n\n// Spawn already in formation, so frame one reads as a murmuration.\nconst boidsInit = agents.makeClusterFormationInit(simLayout, 'boidsInit')\n\n// The physics: flocking, the cursor, and a soft turn at the edges, folded into one update step.\nconst boidsUpdate = integrator.steering2d(simLayout, {\n  forces: [force.flocking(simLayout, {maxAgents: MAX_AGENTS}), force.cursorSteer(simLayout), force.wallTurn(simLayout)],\n  agitation: {rest: 1.5, gain: 0.4545, cool: 1.3},\n  cruiseFloor: 0.35,\n  wallRestitution: 0.6,\n  name: 'boidsUpdate',\n})\n\n// The look: comets along the heading, colored from rest to excited, into a trail canvas.\nconst boidsRender = (shape: string, colorSpace: number) => renderAgents.orientedWorld(simLayout, {\n  shape, res: RES, splatRCap: 16, heading: 'velocity', agitation: 'buffer', comet: true,\n  ramp: {colorSpace, trails: 'on'},\n  names: {splat: 'boidsSplat', resolve: 'boidsResolve'},\n})\n\n// Boids: a living murmuration that flashes toward an excited color when the cursor scatters it.\nexport const Boids = defineShader({\n  name: 'Boids',\n  usesPointer: true,\n  props: {\n    colorA: {default: '#8ec5ff', transform: transformColor},\n    colorB: {default: '#ff7ad9', transform: transformColor},\n    colorSpace: {default: 'oklab', transform: transformColorSpace, compileTime: true, ui: {type: 'select', options: colorSpaceOptions}},\n    agentShape: {default: 'arrow', compileTime: true, ui: {type: 'select', options: agents.orientedShapeOptions}},\n    count: {default: 2000},\n    speed: {default: 2},\n    seed: {default: 0},\n    size: {default: 1.5},\n    trails: {default: 0},\n    perception: {default: 0.12},\n    cursorStrength: {default: 1.5},\n  },\n  ...agentSim<typeof SimParams>({\n    layout: simLayout,\n    params: SimParams,\n    maxAgents: MAX_AGENTS,\n    countCap: {desktop: MAX_AGENTS, mobile: 1200},\n    output: {key: 'outTex', name: 'boidsTexture', size: [RES, RES], format: 'rgba16float'},\n    bake: ({getCpuValue}) => {\n      const render = boidsRender((getCpuValue('agentShape') as string) || 'arrow', (getCpuValue('colorSpace') as number) ?? 2)\n      return {\n        pipelines: {\n          init: {kernel: boidsInit, threads: 'max'},\n          update: {kernel: boidsUpdate, threads: 'agents'},\n          splat: render.splat,\n          resolve: render.resolve,\n        },\n        initStep: 'init',\n        program: ['update', 'splat', 'resolve'],\n      }\n    },\n    frame: (sys, {getCpuValue}) => {\n      const g = makeCpuValueGetter(getCpuValue)\n      return (frameParams) => {\n        const {dt, aspect, pointerX, pointerY} = readAgentFrame(frameParams)\n        const count = sys.resolveCount(g('count', 2000))\n        const maxSpeed = 0.13 * g('speed', 2)\n        const perception = g('perception', 0.12)\n        const colA = getCpuValue('colorA') as {x: number; y: number; z: number; w: number} | undefined\n        const colB = getCpuValue('colorB') as {x: number; y: number; z: number; w: number} | undefined\n        sys.writeParams({\n          colA: d.vec4f(colA?.x ?? 0.56, colA?.y ?? 0.77, colA?.z ?? 1, colA?.w ?? 1),\n          colB: d.vec4f(colB?.x ?? 1, colB?.y ?? 0.48, colB?.z ?? 0.85, colB?.w ?? 1),\n          count, trails: Math.min(Math.max(g('trails', 0), 0), 1) * 0.92,\n          dt, aspect, domainX: Math.max(aspect, 0.01),\n          maxSpeed, maxForce: maxSpeed * 3.5,\n          perceptionSq: perception * perception,\n          sepRadiusSq: (perception * 0.5) * (perception * 0.5),\n          sepW: 1.7, aliW: 1.5, cohW: 1,\n          cursorX: pointerX * aspect, cursorY: pointerY, cursorMode: 2,\n          cursorRadius: 0.22, cursorRadiusSq: 0.22 * 0.22,\n          cursorForce: g('cursorStrength', 1.5) * maxSpeed * 12,\n          bodyR: Math.min(Math.max(g('size', 1.5), 0.5), 3) * 2 / SIZE_REF_RES,\n          margin: 0.07, turnForce: maxSpeed * 2.2,\n          seed: g('seed', 0),\n        })\n        return sys.frame({count, reseed: g('seed', 0)})\n      }\n    },\n    fragment: {output: 'boidsTexture', fallback: 'transparent'},\n  }),\n})","reach":[{"when":"a flock, a swarm, drifting motes, iron filings","use":"`agentSim` with an integrator and a `renderAgents` variant"},{"when":"agents drawn as arrows or streaks along their heading, colored from calm to excited","use":"`renderAgents.orientedWorld`"},{"when":"round motes in one color that twinkle","use":"`renderAgents.pointWorld`"},{"when":"a 3D swarm seen in perspective","use":"`renderAgents.volume`"},{"when":"particles standing on the layer inside, seen through a camera","use":"`renderAgents.relief`"},{"when":"fewer agents on phones without changing the prop's range","use":"the countCap option of `agentSim`"},{"when":"a spawn that re-rolls from a seed prop","use":"the initStep returned by bake, with reseed passed to the frame"}]},"sim.agentForces":{"id":"sim.agentForces","title":"Agent forces","group":"simulation","module":"std/sim/agentForces.ts","access":{"kind":"namespace","name":"sim.agentForces"},"curated":true,"words":["sim.agentForces.integrator","sim.agentForces.integrator.steering2d","sim.agentForces.integrator.forces3d","sim.agentForces.integrator.orientation2d","sim.agentForces.integrator.drift2d","sim.agentForces.integrator.relief","sim.agentForces.integrator.advect2d","sim.agentForces.force","sim.agentForces.force.flocking","sim.agentForces.force.cursorSteer","sim.agentForces.force.wallTurn","sim.agentForces.force.pressure","sim.agentForces.force.containment","sim.agentForces.force.cursorXY","sim.agentForces.force.gravity","sim.agentForces.force.turbulence","sim.agentForces.force.cursorGust","sim.agentForces.force.cursorInView","sim.agentForces.torque","sim.agentForces.torque.alignToField","sim.agentForces.torque.rest","sim.agentForces.field","sim.agentForces.field.dipoleOrRadial","sim.agentForces.field.jitteredGridHome","sim.agentForces.drift","sim.agentForces.drift.variedHeading","sim.agentForces.channel","sim.agentForces.FieldSample","sim.agentForces.channel.alpha","sim.agentForces.channel.blue","sim.agentForces.channel.byMode","sim.agentForces.channel.green","sim.agentForces.channel.luminance","sim.agentForces.channel.luminanceInverted","sim.agentForces.channel.red","sim.agentForces.channel.saturation"],"types":["sim.agentForces.AdvectLayout","sim.agentForces.AgentChannel","sim.agentForces.AgentDrift","sim.agentForces.AgentFieldAt","sim.agentForces.AgentForce3","sim.agentForces.AgentHome2","sim.agentForces.AgentSteer2","sim.agentForces.AgentTorque","sim.agentForces.DriftLayout","sim.agentForces.FieldSampleValue","sim.agentForces.OrientationLayout","sim.agentForces.PressureForce","sim.agentForces.ReliefLayout","sim.agentForces.ShapeField3","sim.agentForces.SteeringLayout","sim.agentForces.Vec4StateArray","sim.agentForces.VolumeSwarmLayout"],"intro":"What pushes, turns and carries each agent. The physics of an agent simulation is a list of\nparts folded into one update step by an **integrator**. A `force` returns a push for one\nagent; a `torque` returns a turn; a `field` samples something at the agent's position (the\nmagnet at the cursor, a home on a grid); a `drift` is a built-in velocity. Every part is built\nover your layout, which names the per-agent buffers and the per-frame values it reads, so the\nsame part works in any layer that provides those names.\n\nSix kinds of agent, six integrators. `integrator.forces3d` moves a 3D swarm held in a shape\n(`force.pressure`, `force.containment`, `force.cursorXY`, `force.gravity`, `force.turbulence`).\n`integrator.steering2d` moves a flock (`force.flocking`, `force.cursorSteer`, `force.wallTurn`).\n`integrator.orientation2d` turns agents in place (`torque.alignToField`, `torque.rest`, over\n`field.dipoleOrRadial` and `field.jitteredGridHome`). `integrator.drift2d` glides motes\n(`drift.variedHeading`, `force.cursorGust`). `integrator.relief` stands particles on the\nchild's image (`channel`, `force.cursorInView`). `integrator.advect2d` carries particles along\na fluid. Pick the integrator, list the parts, and register the result as the update step in\n`agentSim`'s `bake`.\n\nDrag arrives as a per-frame multiplier the frame writes each frame: compute it with\n`agentFrame.expDecay` so it does not depend on frame rate.","example":"import {defineShader, sim, transformColor} from 'shaders/std'\nimport {tgpu, d, agents} from '@coreroot/gpu/kit'\nimport {makeCpuValueGetter, readAgentFrame, resolveRenderRes, SIZE_REF_RES} from '@coreroot/gpu/porters'\n\nconst {agentSim, renderAgents, torque, field, integrator, agentFrame} = sim.agents\n\nconst MAX_FILINGS = 12288\nconst RES = resolveRenderRes({desktop: 1024, mobile: 768})\nconst OVERSCAN = 0.14\n\n// The values the torques, the field and the home read each frame.\nconst SimParams = d.struct({\n  colA: d.vec4f, colB: d.vec4f,\n  count: d.f32,\n  dt: d.f32, aspect: d.f32, domainX: d.f32,\n  cursorX: d.f32, cursorY: d.f32, axisX: d.f32, axisY: d.f32,\n  fieldType: d.f32, restMode: d.f32,\n  strength: d.f32, reachSq: d.f32,\n  alignK: d.f32, damping: d.f32, restK: d.f32, pullK: d.f32, homeK: d.f32,\n  omegaRef: d.f32, agitCool: d.f32,\n  gridCols: d.f32, cellW: d.f32, cellH: d.f32, jitter: d.f32,\n  bodyR: d.f32,\n})\n// Per-agent state (offset from home, angle, spin), excitement, the two canvases, the output.\nconst simLayout = tgpu.bindGroupLayout({\n  agents: {storage: d.arrayOf(d.vec4f, MAX_FILINGS), access: 'mutable'},\n  agit: {storage: d.arrayOf(d.f32, MAX_FILINGS), access: 'mutable'},\n  accumE: {storage: d.arrayOf(d.atomic(d.u32), RES * RES), access: 'mutable'},\n  accumS: {storage: d.arrayOf(d.atomic(d.u32), RES * RES), access: 'mutable'},\n  params: {uniform: SimParams},\n  outTex: {storageTexture: d.textureStorage2d('rgba16float', 'write-only')},\n})\n\nconst filingsInit = agents.makeRestingDirectorInit(simLayout, 'filingsInit')\n\n// The physics: every filing rests on a jittered grid, samples the magnet at the cursor, and\n// swings onto its field line, with a weak pull back to its rest angle where the field is faint.\nconst filingsHome = field.jitteredGridHome(simLayout, {overscan: OVERSCAN})\nconst filingsUpdate = integrator.orientation2d(simLayout, {\n  home: filingsHome,\n  fieldAt: field.dipoleOrRadial(simLayout),\n  torques: [torque.alignToField(simLayout), torque.rest(simLayout)],\n  maxOffset: 0.12,\n  name: 'filingsUpdate',\n})\n\n// The look: slivers drawn along each filing's own angle, colored calm to excited.\nconst filingsRender = renderAgents.orientedWorld(simLayout, {\n  shape: 'streak', res: RES, splatRCap: 14, heading: 'angle', agitation: 'buffer', comet: false, home: filingsHome,\n  ramp: {colorSpace: 2, trails: 'none'},\n  names: {splat: 'filingsSplat', resolve: 'filingsResolve'},\n})\n\n// MagneticFilings: iron filings that reveal the field lines of a magnet at the cursor.\nexport const MagneticFilings = defineShader({\n  name: 'MagneticFilings',\n  usesPointer: true,\n  props: {\n    colorA: {default: '#85929e', transform: transformColor},\n    colorB: {default: '#ff9d5c', transform: transformColor},\n    count: {default: 5000},\n    strength: {default: 1},\n    reach: {default: 0.35},\n    response: {default: 0.5},\n    size: {default: 1},\n  },\n  ...agentSim<typeof SimParams>({\n    layout: simLayout,\n    params: SimParams,\n    maxAgents: MAX_FILINGS,\n    output: {key: 'outTex', name: 'filingsTexture', size: [RES, RES], format: 'rgba16float'},\n    bake: () => ({\n      pipelines: {\n        init: {kernel: filingsInit, threads: 'max'},\n        update: {kernel: filingsUpdate, threads: 'agents'},\n        splat: filingsRender.splat,\n        resolve: filingsRender.resolve,\n      },\n      initStep: 'init',\n      program: ['update', 'splat', 'resolve'],\n    }),\n    frame: (sys, {getCpuValue}) => {\n      const g = makeCpuValueGetter(getCpuValue)\n      // The magnet's axis follows where the cursor has been travelling.\n      const motionAxis = agentFrame.createMotionAxis({smoothing: 0.1, teleport: 0.25})\n      return (frameParams) => {\n        const {dt, aspect, pointerX, pointerY} = readAgentFrame(frameParams)\n        const domainX = Math.max(aspect, 0.01)\n        const count = sys.resolveCount(g('count', 5000))\n        const {cols, cellW, cellH} = agentFrame.fitJitteredGrid(count, domainX, OVERSCAN)\n        const cursorX = pointerX * aspect\n        const cursorY = pointerY\n        const axis = motionAxis.update(cursorX, cursorY)\n        const reach = g('reach', 0.35)\n        const colA = getCpuValue('colorA') as {x: number; y: number; z: number; w: number} | undefined\n        const colB = getCpuValue('colorB') as {x: number; y: number; z: number; w: number} | undefined\n        sys.writeParams({\n          colA: d.vec4f(colA?.x ?? 0.52, colA?.y ?? 0.57, colA?.z ?? 0.64, colA?.w ?? 1),\n          colB: d.vec4f(colB?.x ?? 1, colB?.y ?? 0.62, colB?.z ?? 0.36, colB?.w ?? 1),\n          count,\n          dt, aspect, domainX,\n          cursorX, cursorY, axisX: axis.x, axisY: axis.y,\n          fieldType: 0, restMode: 0,\n          strength: g('strength', 1), reachSq: reach * reach,\n          alignK: 45, damping: 3 + Math.min(Math.max(g('response', 0.5), 0), 1) * 9,\n          restK: 6, pullK: 0.25, homeK: 2.5,\n          omegaRef: 6, agitCool: 1,\n          gridCols: cols, cellW, cellH, jitter: 0.85,\n          bodyR: Math.min(Math.max(g('size', 1), 0.5), 3) * 1.7 / SIZE_REF_RES,\n        })\n        return sys.frame({count})\n      }\n    },\n    fragment: {output: 'filingsTexture', fallback: 'transparent'},\n  }),\n})","reach":[{"when":"a flock that separates, aligns and coheres","use":"`integrator.steering2d` with `force.flocking`"},{"when":"the cursor attracts or scatters a flock","use":"`force.cursorSteer`"},{"when":"a swarm that fills a shape evenly and stays inside it","use":"`integrator.forces3d` with `force.pressure` and `force.containment`"},{"when":"iron filings that swing onto a magnetic field","use":"`integrator.orientation2d` with `torque.alignToField`, `torque.rest`, `field.dipoleOrRadial`"},{"when":"motes that glide in one direction with variety","use":"`integrator.drift2d` with `drift.variedHeading`"},{"when":"a puff from the cursor on drifting motes","use":"`force.cursorGust`"},{"when":"particles that rise by the brightness of the layer inside","use":"`integrator.relief` with `channel`"},{"when":"particles carried along a fluid's velocity","use":"`integrator.advect2d`"}]},"sim.agentFrame":{"id":"sim.agentFrame","title":"Agent frame","group":"simulation","module":"std/sim/agentFrame.ts","access":{"kind":"namespace","name":"sim.agentFrame"},"curated":true,"words":["sim.agentFrame.expDecay","sim.agentFrame.pointerToShapeLocal","sim.agentFrame.createMotionAxis","sim.agentFrame.fitJitteredGrid","sim.agentFrame.fitIsoGrid","sim.agentFrame.cameraRowsYDown","sim.agentFrame.omegaFromRotationDeltas","sim.agentFrame.entrainmentFromOmega","sim.agentFrame.r3SubCellOffset"],"types":[],"intro":"The once-per-frame arithmetic an agent simulation writes into its values. Inside `agentSim`'s\n`frame` callback you turn props and the pointer into the numbers the GPU steps read. These are\nthe recipes that come up in every agent layer, as plain functions with no GPU in them: a drag\nfactor that does not depend on frame rate (`expDecay`), the pointer in the shape's own\ncoordinates (`pointerToShapeLocal`), a grid that fits the agent count (`fitJitteredGrid`,\n`fitIsoGrid`), a direction that follows the cursor's travel (`createMotionAxis`), camera rows\nfor a 3D view (`cameraRowsYDown`), and the rotation bookkeeping a spinning shape needs to\ncarry its swarm along (`omegaFromRotationDeltas`, `entrainmentFromOmega`, `r3SubCellOffset`).\n\nCall them each frame, put the results in the values struct with `sys.writeParams`, and return\n`sys.frame({count})`. Anything stateful (`createMotionAxis`) is created once, outside the\nper-frame function, so each layer instance keeps its own.","example":"import {defineShader, sim, transformColor} from 'shaders/std'\nimport {tgpu, d, agents} from '@coreroot/gpu/kit'\nimport {makeCpuValueGetter, readAgentFrame, resolveRenderRes, SIZE_REF_RES} from '@coreroot/gpu/porters'\n\nconst {agentSim, renderAgents, force, drift, integrator, agentFrame, pointSlot} = sim.agents\n\nconst MAX_MOTES = 8192\nconst RES = resolveRenderRes({desktop: 1024, mobile: 512})\nconst DEG_TO_RAD = Math.PI / 180\n\nconst SimParams = d.struct({\n  color: d.vec4f,\n  count: d.f32, dt: d.f32, time: d.f32, aspect: d.f32, domainX: d.f32,\n  driftBase: d.f32, angleRad: d.f32, speedVar: d.f32, angleVarRad: d.f32,\n  randomness: d.f32, twinkle: d.f32, softness: d.f32,\n  bodyR: d.f32,\n  cursorX: d.f32, cursorY: d.f32, cursorRadSq: d.f32, cursorForce: d.f32, dragMul: d.f32,\n})\nconst simLayout = tgpu.bindGroupLayout({\n  agents: {storage: d.arrayOf(d.vec4f, MAX_MOTES), access: 'mutable'},\n  accumE: {storage: d.arrayOf(d.atomic(d.u32), RES * RES), access: 'mutable'},\n  params: {uniform: SimParams},\n  outTex: {storageTexture: d.textureStorage2d('rgba16float', 'write-only')},\n})\n\nconst motesInit = agents.makeUniformScatterInit(simLayout, 'motesInit')\nconst motesUpdate = integrator.drift2d(simLayout, {drift: drift.variedHeading(simLayout), gust: force.cursorGust(simLayout), name: 'motesUpdate'})\nconst motesRender = renderAgents.pointWorld(simLayout, {\n  shape: 'dot', res: RES,\n  place: pointSlot.orbitalPlace(simLayout, {radius: 0.035, rate: 1.4}),\n  brightness: pointSlot.twinkleBrightness(simLayout, {freq: 2}),\n  bodyRadius: pointSlot.presenceRadius(simLayout),\n  names: {splat: 'motesSplat', resolve: 'motesResolve'},\n})\n\n// Motes: drifting particles the cursor can stir. The frame turns props into the values the steps\n// read; the gust's fade is a rate per second, made frame-rate independent by expDecay.\nexport const Motes = defineShader({\n  name: 'Motes',\n  usesPointer: true,\n  props: {\n    particleColor: {default: '#ffffff', transform: transformColor},\n    count: {default: 1200},\n    speed: {default: 0.25},\n    angle: {default: 90},\n    particleSize: {default: 1.2},\n    cursorStrength: {default: 0.5},\n  },\n  ...agentSim<typeof SimParams>({\n    layout: simLayout,\n    params: SimParams,\n    maxAgents: MAX_MOTES,\n    countCap: {desktop: MAX_MOTES, mobile: 3000},\n    output: {key: 'outTex', name: 'particleTexture', size: [RES, RES], format: 'rgba16float'},\n    bake: () => ({\n      pipelines: {\n        init: {kernel: motesInit, threads: 'max'},\n        update: {kernel: motesUpdate, threads: 'agents'},\n        splat: motesRender.splat,\n        resolve: motesRender.resolve,\n      },\n      initStep: 'init',\n      program: ['update', 'splat', 'resolve'],\n    }),\n    frame: (sys, {getCpuValue}) => {\n      const g = makeCpuValueGetter(getCpuValue)\n      let localTime = 0\n      return (frameParams) => {\n        const {dt, aspect, pointerX, pointerY} = readAgentFrame(frameParams)\n        localTime += dt\n        const count = sys.resolveCount(g('count', 1200))\n        const col = getCpuValue('particleColor') as {x: number; y: number; z: number; w: number} | undefined\n        sys.writeParams({\n          color: d.vec4f(col?.x ?? 1, col?.y ?? 1, col?.z ?? 1, col?.w ?? 1),\n          count, dt, time: localTime, aspect, domainX: Math.max(aspect, 0.01),\n          driftBase: Math.min(Math.max(g('speed', 0.25), 0), 1.5) * 0.48,\n          angleRad: g('angle', 90) * DEG_TO_RAD + Math.PI,\n          speedVar: 0.3,\n          angleVarRad: 30 * DEG_TO_RAD,\n          randomness: 0.25,\n          twinkle: 0.5,\n          softness: 0.1,\n          bodyR: Math.min(Math.max(g('particleSize', 1.2), 0.3), 4) * 2 / SIZE_REF_RES,\n          cursorX: pointerX * aspect, cursorY: pointerY,\n          cursorRadSq: 0.22 * 0.22,\n          cursorForce: Math.min(Math.max(g('cursorStrength', 0.5), 0), 1) * 3.5,\n          // The gust fades at 1.1 per second, whatever the frame rate.\n          dragMul: agentFrame.expDecay(1.1, dt),\n        })\n        return sys.frame({count})\n      }\n    },\n    fragment: {output: 'particleTexture', fallback: 'transparent'},\n  }),\n})","reach":[{"when":"a velocity or gust that fades at a rate per second","use":"`expDecay`"},{"when":"the cursor as a force inside a centered, scaled, rotated shape","use":"`pointerToShapeLocal`"},{"when":"the magnet's axis should follow the cursor's motion","use":"`createMotionAxis`"},{"when":"agents resting on a grid that fits the count and the aspect","use":"`fitJitteredGrid` with `field.jitteredGridHome`"},{"when":"a W×H grid of particles over the canvas","use":"`fitIsoGrid`"},{"when":"a 3D view of the agents from camera angles","use":"`cameraRowsYDown`"},{"when":"a swarm carried along by its rotating shape","use":"`omegaFromRotationDeltas` then `entrainmentFromOmega`"},{"when":"the density grid of `force.pressure` should not line up with the shape","use":"`r3SubCellOffset`"}]},"sim.agentRender":{"id":"sim.agentRender","title":"Agent rendering","group":"simulation","module":"std/sim/agentRender.ts","access":{"kind":"namespace","name":"sim.agentRender"},"curated":true,"words":["sim.agentRender.pointSlot","sim.agentRender.pointSlot.orbitalPlace","sim.agentRender.pointSlot.twinkleBrightness","sim.agentRender.pointSlot.presenceRadius","sim.agentRender.splat","sim.agentRender.splat.orientedWorld","sim.agentRender.splat.pointWorld","sim.agentRender.splat.volume","sim.agentRender.splat.relief","sim.agentRender.resolve","sim.agentRender.resolve.ramp","sim.agentRender.resolve.tint","sim.agentRender.resolve.weightedColor"],"types":["sim.agentRender.OrientedWorldSplatConfig","sim.agentRender.PointWorldSplatConfig","sim.agentRender.PointWorldSplatLayout","sim.agentRender.RampResolveConfig","sim.agentRender.RampResolveLayout","sim.agentRender.ReliefSplatConfig","sim.agentRender.VolumeSplatConfig","sim.agentRender.WorldSplatLayout"],"intro":"How agents become a picture. Every agent frame ends the same way: each agent stamps its shape\ninto a shared canvas (the **splat**), then one pass turns the canvas into the output picture\nand clears it for the next frame (the **resolve**). `renderAgents` in `sim.agents` pairs a\nsplat with its resolve for you; reach into this module when you need another pairing, or for\nthe `pointSlot` parts that give round motes their placement, brightness and size.\n\nFour splats match the four kinds of agent: `splat.orientedWorld` draws a shape along the\nagent's heading in canvas units; `splat.pointWorld` draws round motes with a live softness;\n`splat.volume` draws a 3D swarm in perspective; `splat.relief` draws particles standing on an\nimage through a camera. Three resolves close them: `resolve.ramp` colors from a rest color to\nan excited color, with optional trails; `resolve.tint` gives one color an alpha from coverage;\n`resolve.weightedColor` averages the colors that landed, nearest first. A splat dispatches over\nthe agents; a resolve dispatches over every texel of the canvas, because it also clears it.","example":"import {defineShader, sim, transformColor} from 'shaders/std'\nimport {tgpu, d, agents} from '@coreroot/gpu/kit'\nimport {makeCpuValueGetter, readAgentFrame, resolveRenderRes, SIZE_REF_RES} from '@coreroot/gpu/porters'\n\nconst {agentSim, force, drift, integrator, agentFrame} = sim.agents\nconst {splat, resolve, pointSlot} = sim.agentRender\n\nconst MAX_MOTES = 8192\nconst RES = resolveRenderRes({desktop: 1024, mobile: 512})\n\nconst SimParams = d.struct({\n  color: d.vec4f,\n  count: d.f32, dt: d.f32, time: d.f32, aspect: d.f32, domainX: d.f32,\n  driftBase: d.f32, angleRad: d.f32, speedVar: d.f32, angleVarRad: d.f32,\n  randomness: d.f32, twinkle: d.f32, softness: d.f32,\n  bodyR: d.f32,\n  cursorX: d.f32, cursorY: d.f32, cursorRadSq: d.f32, cursorForce: d.f32, dragMul: d.f32,\n})\nconst simLayout = tgpu.bindGroupLayout({\n  agents: {storage: d.arrayOf(d.vec4f, MAX_MOTES), access: 'mutable'},\n  accumE: {storage: d.arrayOf(d.atomic(d.u32), RES * RES), access: 'mutable'},\n  params: {uniform: SimParams},\n  outTex: {storageTexture: d.textureStorage2d('rgba16float', 'write-only')},\n})\n\nconst motesInit = agents.makeUniformScatterInit(simLayout, 'motesInit')\nconst motesUpdate = integrator.drift2d(simLayout, {drift: drift.variedHeading(simLayout), gust: force.cursorGust(simLayout), name: 'motesUpdate'})\n\n// The look, paired by hand: a point splat whose placement, brightness and size are pointSlot\n// parts, closed by the tint resolve over the same canvas.\nconst motesSplat = splat.pointWorld(simLayout, {\n  shape: 'glow', res: RES,\n  place: pointSlot.orbitalPlace(simLayout, {radius: 0.035, rate: 1.4}),\n  brightness: pointSlot.twinkleBrightness(simLayout, {freq: 2}),\n  bodyRadius: pointSlot.presenceRadius(simLayout),\n  name: 'motesSplat',\n})\nconst motesResolve = resolve.tint(simLayout, {res: RES, name: 'motesResolve'})\n\n// Motes: soft drifting points that twinkle and circle lazily around their path.\nexport const Motes = defineShader({\n  name: 'Motes',\n  usesPointer: true,\n  props: {\n    particleColor: {default: '#ffffff', transform: transformColor},\n    count: {default: 1200},\n    speed: {default: 0.25},\n    randomness: {default: 0.25},\n    twinkle: {default: 0.5},\n    softness: {default: 0.1},\n    particleSize: {default: 1.2},\n  },\n  ...agentSim<typeof SimParams>({\n    layout: simLayout,\n    params: SimParams,\n    maxAgents: MAX_MOTES,\n    countCap: {desktop: MAX_MOTES, mobile: 3000},\n    output: {key: 'outTex', name: 'particleTexture', size: [RES, RES], format: 'rgba16float'},\n    bake: () => ({\n      pipelines: {\n        init: {kernel: motesInit, threads: 'max'},\n        update: {kernel: motesUpdate, threads: 'agents'},\n        splat: {kernel: motesSplat, threads: 'agents'},\n        resolve: {kernel: motesResolve, threads: 'fixed', size: [RES, RES]},\n      },\n      initStep: 'init',\n      program: ['update', 'splat', 'resolve'],\n    }),\n    frame: (sys, {getCpuValue}) => {\n      const g = makeCpuValueGetter(getCpuValue)\n      let localTime = 0\n      return (frameParams) => {\n        const {dt, aspect, pointerX, pointerY} = readAgentFrame(frameParams)\n        localTime += dt\n        const count = sys.resolveCount(g('count', 1200))\n        const col = getCpuValue('particleColor') as {x: number; y: number; z: number; w: number} | undefined\n        sys.writeParams({\n          color: d.vec4f(col?.x ?? 1, col?.y ?? 1, col?.z ?? 1, col?.w ?? 1),\n          count, dt, time: localTime, aspect, domainX: Math.max(aspect, 0.01),\n          driftBase: Math.min(Math.max(g('speed', 0.25), 0), 1.5) * 0.48,\n          angleRad: Math.PI * 1.5,\n          speedVar: 0.3,\n          angleVarRad: Math.PI / 6,\n          randomness: Math.min(Math.max(g('randomness', 0.25), 0), 1),\n          twinkle: Math.min(Math.max(g('twinkle', 0.5), 0), 1),\n          softness: Math.min(Math.max(g('softness', 0.1), 0), 1),\n          bodyR: Math.min(Math.max(g('particleSize', 1.2), 0.3), 4) * 2 / SIZE_REF_RES,\n          cursorX: pointerX * aspect, cursorY: pointerY,\n          cursorRadSq: 0.22 * 0.22,\n          cursorForce: 0,\n          dragMul: agentFrame.expDecay(1.1, dt),\n        })\n        return sys.frame({count})\n      }\n    },\n    fragment: {output: 'particleTexture', fallback: 'transparent'},\n  }),\n})","reach":[{"when":"a splat and resolve paired for you","use":"`renderAgents`"},{"when":"motes that wander in small circles, twinkle, and vary in size","use":"`pointSlot.orbitalPlace`, `pointSlot.twinkleBrightness`, `pointSlot.presenceRadius`"},{"when":"arrows, streaks or comets along the heading","use":"`splat.orientedWorld` with `resolve.ramp`"},{"when":"round motes in one color","use":"`splat.pointWorld` with `resolve.tint`"},{"when":"a 3D swarm, nearer particles larger and brighter","use":"`splat.volume` with `resolve.ramp`"},{"when":"particles carrying the child's color, nearer ones in front","use":"`splat.relief` with `resolve.weightedColor`"},{"when":"motion trails behind the agents","use":"the trails option of `resolve.ramp`"}]},"sim.shapeFields":{"id":"sim.shapeFields","title":"Shape fields","group":"simulation","module":"std/sim/shapeFields.ts","access":{"kind":"namespace","name":"sim.shapeFields"},"curated":true,"words":["sim.shapeFields.shapeField","sim.shapeFields.shapeField.analyticExtruded","sim.shapeFields.shapeField.svgExtruded","sim.shapeFields.shapeField.analytic3d","sim.shapeFields.shapeField.svgLifted3d"],"types":[],"intro":"The shape a swarm is held inside. `force.containment` needs one thing from a shape: the signed\ndistance to its surface at any 3D point, negative inside and positive outside. `shapeField`\nbuilds that distance function from whatever shape the layer was given, so one integrator serves\nevery shape kind: `analyticExtruded` gives a flat shape (circle, polygon, star) a depth;\n`svgExtruded` does the same for an SVG outline uploaded as a distance texture; `analytic3d`\nwraps a true 3D solid; `svgLifted3d` wraps an SVG lifted into 3D. The last two rotate with the\nshape's live rotation, so the swarm follows the surface as it turns.\n\nCoordinates are shape-local: the shape sits in a cube from −0.5 to 0.5 with y up, and the\nlayer's center, scale and rotation are applied when the swarm is drawn. Pick the builder in\n`agentSim`'s `bake` from the shape props, hand one field to `force.containment`, and the\nintegrator never needs to know which kind it was. The flat builders read the shape's sub-props\nand half-depth from the values struct each frame.","example":"import {defineShader, sim, transformColor} from 'shaders/std'\nimport {tgpu, d, sdf, agents} from '@coreroot/gpu/kit'\nimport {makeCpuValueGetter, readAgentFrame, resolveRenderRes, SIZE_REF_RES} from '@coreroot/gpu/porters'\n\nconst {agentSim, renderAgents, force, integrator, agentFrame, shapeField} = sim.agents\n\nconst MAX_PARTICLES = 16000\nconst DGRID = 32\nconst DOMAIN = 1.0\nconst OUT_RES = resolveRenderRes({desktop: 1024, mobile: 768})\nconst MAX_SPEED = 3\n\nconst SimParams = d.struct({\n  colA: d.vec4f, colB: d.vec4f,\n  dt: d.f32, time: d.f32, spread: d.f32, agitation: d.f32,\n  dragMul: d.f32, gravX: d.f32, gravY: d.f32, halfDepth: d.f32,\n  cursorX: d.f32, cursorY: d.f32, cursorForce: d.f32, cursorRadSq: d.f32,\n  saRadius: d.f32, saSides: d.f32, saRounding: d.f32, saInnerRatio: d.f32,\n  saRotation: d.f32, saHeight: d.f32, saOffset: d.f32, saAperture: d.f32,\n  centerX: d.f32, centerYv: d.f32, scale: d.f32, rotC: d.f32, rotS: d.f32, aspect: d.f32,\n  size: d.f32, exposure: d.f32, softness: d.f32, speedColorK: d.f32,\n  omegaX: d.f32, omegaY: d.f32, omegaZ: d.f32, entrain: d.f32,\n  gridOffX: d.f32, gridOffY: d.f32, gridOffZ: d.f32,\n})\nconst simLayout = tgpu.bindGroupLayout({\n  pos: {storage: d.arrayOf(d.vec4f, MAX_PARTICLES), access: 'mutable'},\n  vel: {storage: d.arrayOf(d.vec4f, MAX_PARTICLES), access: 'mutable'},\n  dens: {storage: d.arrayOf(d.atomic(d.u32), DGRID * DGRID * DGRID), access: 'mutable'},\n  accumE: {storage: d.arrayOf(d.atomic(d.u32), OUT_RES * OUT_RES), access: 'mutable'},\n  accumS: {storage: d.arrayOf(d.atomic(d.u32), OUT_RES * OUT_RES), access: 'mutable'},\n  params: {uniform: SimParams},\n  outTex: {storageTexture: d.textureStorage2d('rgba16float', 'write-only')},\n})\n\nconst swarmInit = agents.makeBallCloudInit(simLayout, 'swarmInit')\n// Even filling: particles are counted into a density grid and slide away from crowding.\nconst pressure = force.pressure(simLayout, {gridDim: DGRID, domain: DOMAIN, names: {clear: 'swarmClearDensity', splat: 'swarmDensity'}})\n\n// A hexagon with depth, as the field the containment force reads. Its radius, sides and rounding\n// arrive through the values struct each frame.\nconst hexField = shapeField.analyticExtruded(simLayout, sdf.buildAnalyticSdfFn('polygonSDF'))\nconst swarmUpdate = integrator.forces3d(simLayout, {\n  forces: [\n    pressure.force,\n    force.containment(simLayout, {\n      field: hexField,\n      gradEps: 0.02,\n      wall: {featherFrom: -0.10, featherTo: 0.01, base: 1.1, springK: 22},\n      recall: {from: DOMAIN * 0.85, to: DOMAIN * 1.1, k: 9},\n      homing: {from: 0.12, to: 0.45, k: 5},\n      entrainment: {featherHalf: 0.05},\n    }),\n    force.cursorXY(simLayout),\n    force.gravity(simLayout),\n    force.turbulence(simLayout, {posScale: 193, timeX: 31.7, timeY: 27.3, seedScale: 997, gain: 5}),\n  ],\n  maxSpeed: MAX_SPEED,\n  posClamp: 1.9,\n  name: 'swarmUpdate',\n})\nconst swarmRender = renderAgents.volume(simLayout, {\n  shape: 'dot', outRes: OUT_RES, maxSplatSize: 6, extQ: 2.6,\n  ramp: {colorSpace: 0, exposure: true, trails: 'none'},\n  names: {splat: 'swarmSplat', glow: 'swarmGlowProfile', profile: 'swarmProfile', resolve: 'swarmResolve'},\n})\n\n// HexSwarm: particles that fill a hexagon evenly and scatter when the cursor pushes through them.\nexport const HexSwarm = defineShader({\n  name: 'HexSwarm',\n  usesPointer: true,\n  props: {\n    colorA: {default: '#ffffff', transform: transformColor},\n    colorB: {default: '#ff7ad9', transform: transformColor},\n    count: {default: 4000},\n    radius: {default: 0.35},\n    depth: {default: 0.18},\n    spread: {default: 1},\n    damping: {default: 0.4},\n    mouseInfluence: {default: 1.2},\n  },\n  ...agentSim<typeof SimParams>({\n    layout: simLayout,\n    params: SimParams,\n    maxAgents: MAX_PARTICLES,\n    countCap: {desktop: MAX_PARTICLES, mobile: 6000},\n    output: {key: 'outTex', name: 'particleTexture', size: [OUT_RES, OUT_RES], format: 'rgba16float'},\n    bake: () => ({\n      pipelines: {\n        init: {kernel: swarmInit, threads: 'max'},\n        clearDensity: {kernel: pressure.clearKernel, threads: 'fixed', size: [pressure.cells]},\n        density: {kernel: pressure.splatKernel, threads: 'agents'},\n        update: {kernel: swarmUpdate, threads: 'agents'},\n        splat: swarmRender.splat,\n        resolve: swarmRender.resolve,\n      },\n      initStep: 'init',\n      program: ['clearDensity', 'density', 'update', 'splat', 'resolve'],\n    }),\n    frame: (sys, {getCpuValue}) => {\n      const g = makeCpuValueGetter(getCpuValue)\n      let time = 0\n      let frameIdx = 0\n      const CELL = (2 * DOMAIN) / DGRID\n      return (frameParams) => {\n        const {dt, aspect, pointerX, pointerY} = readAgentFrame(frameParams, 1)\n        time += dt\n        const centerX = 0.5, centerYv = 0.5, scale = 1, rotC = 1, rotS = 0\n        // The pointer in the shape's own coordinates, so the cursor force acts where the swarm lives.\n        const cursor = agentFrame.pointerToShapeLocal({pointerX, pointerY, centerX, centerYv, scale, rotC, rotS, aspect})\n        const gridOff = agentFrame.r3SubCellOffset(frameIdx++, CELL)\n        const colA = getCpuValue('colorA') as {x: number; y: number; z: number; w: number} | undefined\n        const colB = getCpuValue('colorB') as {x: number; y: number; z: number; w: number} | undefined\n        sys.writeParams({\n          colA: d.vec4f(colA?.x ?? 1, colA?.y ?? 1, colA?.z ?? 1, colA?.w ?? 1),\n          colB: d.vec4f(colB?.x ?? 1, colB?.y ?? 0.48, colB?.z ?? 0.85, colB?.w ?? 1),\n          dt, time,\n          spread: g('spread', 1),\n          agitation: 0.12,\n          dragMul: agentFrame.expDecay(2.2 + g('damping', 0.4) * 6, dt),\n          gravX: 0, gravY: 0,\n          halfDepth: Math.max(g('depth', 0.18), 0.01) * 0.5,\n          cursorX: cursor.x, cursorY: cursor.y,\n          cursorForce: g('mouseInfluence', 1.2) * 3,\n          cursorRadSq: 0.22 * 0.22,\n          saRadius: g('radius', 0.35), saSides: 6, saRounding: 0, saInnerRatio: 0.4,\n          saRotation: 0, saHeight: 0.25, saOffset: 0.2, saAperture: 270,\n          centerX, centerYv, scale, rotC, rotS, aspect,\n          size: 2.2 * OUT_RES / SIZE_REF_RES,\n          exposure: 1,\n          softness: 0.5,\n          speedColorK: 2 / MAX_SPEED,\n          omegaX: 0, omegaY: 0, omegaZ: 0, entrain: 0,\n          gridOffX: gridOff.x, gridOffY: gridOff.y, gridOffZ: gridOff.z,\n        })\n        return sys.frame({count: sys.resolveCount(g('count', 4000))})\n      }\n    },\n    fragment: {output: 'particleTexture', fallback: 'transparent'},\n  }),\n})","reach":[{"when":"a swarm held inside a flat shape with some depth","use":"`shapeField.analyticExtruded`"},{"when":"a swarm held inside an SVG outline with some depth","use":"`shapeField.svgExtruded`"},{"when":"a swarm inside a sphere, torus or other 3D solid that rotates","use":"`shapeField.analytic3d`"},{"when":"a swarm inside an SVG lifted into 3D","use":"`shapeField.svgLifted3d`"},{"when":"the force that reads the field","use":"`force.containment`"}]}},"words":{"define.defineShader":{"id":"define.defineShader","name":"defineShader","qualifiedName":"defineShader","category":"define","module":"std/lower.ts","summary":"Define a shader component from a plain object: its props, and one field that says what it draws.","description":"The field carrying the per-pixel work decides what kind of component you get. `paint:` makes a generator: it paints from coordinates and needs nothing inside it. `effect:` makes a filter over the layer inside it (the child). A `wgsl` body that only reads `child` edits one pixel at a time; one that samples `childTexture` reads neighbours through its own render pass. `map:` makes a distortion from one coordinate function, `shape:` a 2D shape with fill and stroke, `gpu:` a raw builder. Give exactly one. `role` and `species` are optional and may only restate what the field already says.\n\n`props` are the component's attributes. Each has a `default`, an optional `transform` (`transformColor`, `transformPosition`, …), optional `ui` metadata for editors, and `compileTime: true` when a change should rebuild the shader (or `recompile: crosses(0)` to rebuild only at a threshold). A prop name must be a plain identifier and must not be one of the layer props every component already has — `blendMode`, `opacity`, `visible`, `id`, `maskSource`, `maskType`, `renderOrder`, `transform`, `boundingBox`, `flow`, `absolute`, `children`, `ref`, `key`, `src` — nor a name the renderer manages itself (`_animTime`, `_opacity`, or anything starting `_pad`, `_bbox_`, `_map_`). Those throw when the definition is built. `animatedTime: {speed: 'speed'}` gives the layer its own clock, scaled by that prop (0 pauses it); `time` in a `wgsl` body and `animatedTime(params)` in a builder read it.\n\nThe returned definition is what you hand to `<CustomShader src={Halo}>` in React, Vue, Svelte or Solid, to `registerShader`, or to `createShader(canvas, preset, {components: [Halo]})` so preset JSON can name it by `type`.","examples":["import {defineShader, wgsl, transformColor, transformPosition} from 'shaders/std'\n\nexport const Halo = defineShader({\n  name: 'Halo',\n  animatedTime: {speed: 'speed'},\n  props: {\n    inner: {default: '#ffd166', transform: transformColor},\n    outer: {default: '#0b132b', transform: transformColor},\n    center: {default: {x: 0.5, y: 0.5}, transform: transformPosition},\n    radius: {default: 0.6},\n    bands: {default: 4},\n    speed: {default: 1},\n  },\n  paint: wgsl`\n    let d = length((uv - center) * vec2f(aspect, 1.0)) / radius;\n    let wave = 0.5 + 0.5 * cos(d * bands * 6.2831853 - time * 2.0);\n    return vec4f(mix(outer.rgb, inner.rgb, wave * (1.0 - smoothstep(0.7, 1.0, d))), 1.0);\n  `,\n})","// A filter composed from std words, skipped entirely while `intensity` is 0.\nexport const Vignette = defineShader({\n  name: 'Vignette',\n  props: {\n    color: {default: '#000000', transform: transformColor},\n    center: {default: {x: 0.5, y: 0.5}, transform: transformPosition},\n    radius: {default: 0.5},\n    falloff: {default: 0.5},\n    intensity: {default: 1, recompile: crosses(0)},\n  },\n  effect: tintToward(p('color'), {amount: radialMask({center: p('center'), radius: p('radius'), falloff: p('falloff')}).times(p('intensity'))}),\n  identityWhen: isZero('intensity'),\n})"],"tips":["A prop the engine never sends to the GPU (a URL string, a shape object, a list) cannot be read in a `wgsl` body; reference it and WGSL reports the name as undefined."],"see":["define.wgsl","define.registerShader","values.p","slots.crosses","slots.isZero","lists.listOf"],"import":{"statement":"import {defineShader} from 'shaders/std'","usage":"defineShader"},"usedBy":[],"documented":true,"source":{"file":"std/lower.ts","line":528},"kind":"function","signature":"defineShader<T extends ComponentProps>(definition: StdDefinition<T>): GpuShaderDefinition<T>","params":[{"name":"definition","type":"StdDefinition<T>","optional":false}]},"define.WgslBody":{"id":"define.WgslBody","name":"WgslBody","qualifiedName":"WgslBody","category":"define","module":"std/wgsl.ts","summary":"What `wgsl` returns: a body ready to sit in `paint:` or `effect:`.","description":"","examples":[],"tips":[],"see":[],"import":{"statement":"import {WgslBody} from 'shaders/std'","usage":"WgslBody"},"usedBy":[],"documented":true,"source":{"file":"std/wgsl.ts","line":76},"kind":"class","signature":"class WgslBody","params":[]},"define.wgsl":{"id":"define.wgsl","name":"wgsl","qualifiedName":"wgsl","category":"define","module":"std/wgsl.ts","summary":"Write the per-pixel math by hand, as one WGSL function body that returns a `vec4f` color.","description":"Put it in `paint:` for a generator or `effect:` for a filter. Every name the body uses is bound for you. The definition's props come by name: colors as `vec4f` in linear RGB with alpha, positions as `vec2f` in uv, numbers and booleans as `f32`, select props as their numeric value. The context comes as `uv` (0–1 across the canvas, y down), `time` (seconds; the layer's own clock when `animatedTime` is declared, paused at speed 0), `aspect` (width / height), `viewport` (the frame size in device pixels) and `pointer` (the mouse in uv). In an `effect:` body `child` is the color of the layer inside at this pixel. Sample `childTexture` with `childSampler` instead when you need neighbouring pixels (a blur, a displacement): that gives the filter its own render pass and hands you premultiplied color, which is unpremultiplied for you on the way out. Template values (`${OCTAVES}`) are pasted in as text, so a constant can be a loop bound.\n\nUse the `{body, inputs, alpha}` form to bind extra names (`inputs: {t: ctx.time, k: 4}`) or to say a gather body already returns straight alpha (`alpha: 'straight'`).","examples":["paint: wgsl`\n  let d = length((uv - center) * vec2f(aspect, 1.0)) / radius;\n  return vec4f(mix(outer.rgb, inner.rgb, 1.0 - smoothstep(0.0, 1.0, d)), 1.0);\n`","// A gather filter: the layer inside, sampled at a rippled coordinate.\neffect: wgsl`\n  let wave = sin(length(uv - center) * frequency - time * 4.0) * amplitude;\n  return textureSample(childTexture, childSampler, uv + wave);\n`"],"tips":["The names `uv`, `time`, `aspect`, `viewport`, `pointer`, `child`, `childTexture` and `childSampler` belong to the body; a prop spelled the same is not bound, so rename the prop."],"see":["define.defineShader","invoke.ctx","values.p"],"import":{"statement":"import {wgsl} from 'shaders/std'","usage":"wgsl"},"usedBy":[],"documented":true,"source":{"file":"std/wgsl.ts","line":159},"kind":"function","signature":"wgsl(spec: WgslSpec): WgslBody\nwgsl(body: string): WgslBody\nwgsl(strings: TemplateStringsArray, ...values: unknown[]): WgslBody","params":[{"name":"spec","type":"WgslSpec","optional":false}]},"define.registerShader":{"id":"define.registerShader","name":"registerShader","qualifiedName":"registerShader","category":"define","module":"customShaders.ts","summary":"Make a user-defined shader findable by its `name`, so preset JSON can reference it as `type: 'Halo'`.","description":"Returns the definition, so it can wrap `defineShader` inline. Registering the same object again does nothing. A different definition under a taken name replaces it (live editing, hot reload) and notifies `onShaderRegistered` listeners. `<CustomShader>` calls this for you on mount; `createShader(canvas, preset, {components: [Halo]})` is the other way to make a name resolvable without registering globally.","examples":["import {createShader} from 'shaders/js'\nimport {Halo} from './halo'\n\nregisterShader(Halo)\nconst shader = await createShader(canvas, {components: [{type: 'Halo', props: {bands: 6}}]})"],"tips":[],"see":["define.defineShader","define.unregisterShader","define.getRegisteredShader","define.onShaderRegistered"],"import":{"statement":"import {registerShader} from 'shaders/std'","usage":"registerShader"},"usedBy":[],"documented":true,"source":{"file":"customShaders.ts","line":37},"kind":"function","signature":"registerShader<T extends GpuShaderDefinition<any>>(definition: T): T","params":[{"name":"definition","type":"T","optional":false}]},"define.unregisterShader":{"id":"define.unregisterShader","name":"unregisterShader","qualifiedName":"unregisterShader","category":"define","module":"customShaders.ts","summary":"Forget a user-defined shader by name.","description":"Does nothing for a name that was never registered. Listeners from `onShaderRegistered` are told with a `null` definition.","examples":["unregisterShader('Halo')"],"tips":[],"see":["define.registerShader"],"import":{"statement":"import {unregisterShader} from 'shaders/std'","usage":"unregisterShader"},"usedBy":[],"documented":true,"source":{"file":"customShaders.ts","line":64},"kind":"function","signature":"unregisterShader(name: string): void","params":[{"name":"name","type":"string","optional":false}]},"define.getRegisteredShader":{"id":"define.getRegisteredShader","name":"getRegisteredShader","qualifiedName":"getRegisteredShader","category":"define","module":"customShaders.ts","summary":"Look up a user-defined shader by name.","description":"Returns `undefined` for unknown names. The library's own shaders are not in this registry.","examples":["const definition = getRegisteredShader('Halo')"],"tips":[],"see":["define.getRegisteredShaders","define.registerShader"],"import":{"statement":"import {getRegisteredShader} from 'shaders/std'","usage":"getRegisteredShader"},"usedBy":[],"documented":true,"source":{"file":"customShaders.ts","line":80},"kind":"function","signature":"getRegisteredShader(name: string): GpuShaderDefinition | undefined","params":[{"name":"name","type":"string","optional":false}]},"define.getRegisteredShaders":{"id":"define.getRegisteredShaders","name":"getRegisteredShaders","qualifiedName":"getRegisteredShaders","category":"define","module":"customShaders.ts","summary":"Every user-defined shader currently registered, oldest first.","description":"","examples":["const names = getRegisteredShaders().map((d) => d.name)"],"tips":[],"see":["define.getRegisteredShader","define.onShaderRegistered"],"import":{"statement":"import {getRegisteredShaders} from 'shaders/std'","usage":"getRegisteredShaders"},"usedBy":[],"documented":true,"source":{"file":"customShaders.ts","line":93},"kind":"function","signature":"getRegisteredShaders(): GpuShaderDefinition[]","params":[]},"define.onShaderRegistered":{"id":"define.onShaderRegistered","name":"onShaderRegistered","qualifiedName":"onShaderRegistered","category":"define","module":"customShaders.ts","summary":"Be told when a user-defined shader is registered, replaced or removed.","description":"The listener gets the name and the new definition, or `null` on removal. Returns a function that unsubscribes. Useful for a component picker or a live editor that lists custom shaders.","examples":["const stop = onShaderRegistered((name, definition) => refreshPicker())"],"tips":[],"see":["define.registerShader","define.getRegisteredShaders"],"import":{"statement":"import {onShaderRegistered} from 'shaders/std'","usage":"onShaderRegistered"},"usedBy":[],"documented":true,"source":{"file":"customShaders.ts","line":109},"kind":"function","signature":"onShaderRegistered(listener: (name: string, definition: GpuShaderDefinition | null) => void): () => void","params":[{"name":"listener","type":"(name: string, definition: GpuShaderDefinition | null) => void","optional":false}]},"values.p":{"id":"values.p","name":"p","qualifiedName":"p","category":"values","module":"std/values.ts","summary":"Reference one of the definition's props by name, wherever a word asks for a value.","description":"Pass it where a std word takes a color, a position or a number and the word reads that prop live on the GPU. The name must be one of the definition's `props`; a wrong name throws when the shader is built.","examples":["effect: tintToward(p('color'), {amount: p('intensity')})"],"tips":["A prop ref is a binding, not a snapshot: changing the prop later needs no rebuild."],"see":["values.Scalar","invoke.uniformOf","invoke.ctx"],"import":{"statement":"import {p} from 'shaders/std'","usage":"p"},"usedBy":["AngularBlur","Ascii","Aurora","BarShift","BarnDoors","Beam","Bend","Blob","BlockDissolve","BlockNoise","BlueNoise","Blur","BokehBlur","BrickPattern","BrightnessContrast","Bulge","CRTScreen","Chalkboard","ChannelBlur","CheckerWipe","Checkerboard","Chevron","ChromaticAberration","ColorWheel","CompressionArtifacts","ConcentricSpin","ConicGradient","ContourLines","CornerPin","CurlNoise","CursorRipples","DiamondGradient","DiamondWipe","DiffuseBlur","DisplacementMap","Dither","DotGrid","DropShadow","Duotone","Engraving","ErosionNoise","Exposure","FallingLines","FilmGrain","FilmStock","Flip","FlowField","FlowingGradient","FlutedGlass","FractalNoise","GaborNoise","GlassTiles","Glitch","Glow","Godrays","GradientMap","Grid","GridDistortion","Halftone","HexGrid","HueShift","ImageTexture","IrisWipe","IsometricCubes","Kaleidoscope","KeyFrames","LensDistortion","LensFlare","LightLeak","Line","LinearBlur","LinearGradient","LinearWipe","Liquify","Marble","MeshGradient","Mirror","MultiPointGradient","NoiseDissolve","ObjectTracker","Obsidian","PagePeel","PerlinNoise","Perspective","Pixelate","Plasma","PolarCoordinates","Posterize","Prism","ProgressiveBlur","RadialGradient","RadialWipe","RandomBars","RectangularCoordinates","ReflectivePlane","Repeater","RippleWipe","Ripples","Satin","Saturation","Scratches","Sharpness","SimplexNoise","SineWave","SliceWipe","Solarize","SolidColor","Spherize","Spiral","Strands","Stretch","Stripes","StudioBackground","SunBurst","Swirl","TiltShift","Tint","TriangularGrid","Tritone","Truchet","Twirl","VHS","VenetianBlinds","Vibrance","VideoTexture","Vignette","Voronoi","Voxels","Watercolor","WaveDistortion","Waveform","WaveletNoise","Weave","WebcamTexture","WorleyNoise","ZoomBlur"],"documented":true,"source":{"file":"std/values.ts","line":35},"kind":"function","signature":"p(name: string): PropRef","params":[{"name":"name","type":"string","optional":false}]},"values.Scalar":{"id":"values.Scalar","name":"Scalar","qualifiedName":"Scalar","category":"values","module":"std/values.ts","summary":"A number built from props: what `radialMask(…)`, `motion.oscillating(…)` and `.times()` return.","description":"Pass it anywhere a word accepts a `ScalarInput` (a prop ref or a Scalar): an effect's `amount`, a mask's strength. Chain `.times(p('intensity'))` to scale it by a prop.","examples":["effect: tintToward(p('color'), {amount: radialMask({center: p('center'), radius: p('radius'), falloff: p('falloff')}).times(p('intensity'))})"],"tips":[],"see":["values.p","invoke.resolveScalar"],"import":{"statement":"import {Scalar} from 'shaders/std'","usage":"Scalar"},"usedBy":[],"documented":true,"source":{"file":"std/values.ts","line":66},"kind":"class","signature":"class Scalar","params":[]},"slots.crosses":{"id":"slots.crosses","name":"crosses","qualifiedName":"crosses","category":"slots","module":"std/slots.ts","summary":"Rebuild the shader only when the prop crosses `value`, not on every change.","description":"Put it on a prop's `recompile:`. Pair it with an identity rule at the same value: the rule decides whether the filter is skipped, and this is what lets that decision change.","examples":["props: {shift: {default: 0, recompile: crosses(0)}},\neffect: hueRotate(p('shift')),\nidentityWhen: isZero('shift'),"],"tips":["Without it a runtime prop never rebuilds, so a filter that started skipped stays skipped."],"see":["slots.isZero","slots.isValue","slots.recompileWhen"],"import":{"statement":"import {crosses} from 'shaders/std'","usage":"crosses"},"usedBy":["AngularBlur","BrightnessContrast","DiffuseBlur","Exposure","FilmGrain","FilmStock","Glow","GradientMap","HueShift","LinearBlur","MeshGradient","ProgressiveBlur","Repeater","Saturation","Sharpness","Solarize","Sparkle","TiltShift","Vibrance","Vignette","Watercolor","ZoomBlur"],"documented":true,"source":{"file":"std/slots.ts","line":32},"kind":"function","signature":"crosses(value: number): RecompileRule","params":[{"name":"value","type":"number","optional":false}]},"slots.recompileWhen":{"id":"slots.recompileWhen","name":"recompileWhen","qualifiedName":"recompileWhen","category":"slots","module":"std/slots.ts","summary":"Rebuild when your own test of the old and new value says so, for a threshold `crosses` cannot express.","description":"","examples":["blur: {default: 0, recompile: recompileWhen((prev, next) => (prev > 0) !== (next > 0))}"],"tips":[],"see":["slots.crosses"],"import":{"statement":"import {recompileWhen} from 'shaders/std'","usage":"recompileWhen"},"usedBy":["FlutedGlass","Glass","GridDistortion","ParticleFlow","Surface3D"],"documented":true,"source":{"file":"std/slots.ts","line":46},"kind":"function","signature":"recompileWhen(predicate: (prev: any, next: any) => boolean): RecompileRule","params":[{"name":"predicate","type":"(prev: any, next: any) => boolean","optional":false}]},"slots.isZero":{"id":"slots.isZero","name":"isZero","qualifiedName":"isZero","category":"slots","module":"std/slots.ts","summary":"Skip the filter entirely while `prop` is exactly 0.","description":"Put it on `identityWhen:`. An unset prop counts as its default. A prop that is mapped per pixel by a driver disables the skip automatically, since the value then varies across the canvas. Give the prop `recompile: crosses(0)` so the skip switches on and off.","examples":["identityWhen: isZero('intensity')"],"tips":[],"see":["slots.isValue","slots.allOf","slots.crosses"],"import":{"statement":"import {isZero} from 'shaders/std'","usage":"isZero"},"usedBy":["BrightnessContrast","FilmGrain","GradientMap","HueShift","Sharpness","Solarize","Sparkle","Vibrance","Vignette"],"documented":true,"source":{"file":"std/slots.ts","line":63},"kind":"function","signature":"isZero(prop: string): IdentityRule","params":[{"name":"prop","type":"string","optional":false}]},"slots.isValue":{"id":"slots.isValue","name":"isValue","qualifiedName":"isValue","category":"slots","module":"std/slots.ts","summary":"Skip the filter entirely while `prop` equals `value`, for a filter whose no-op is not 0.","description":"An unset prop counts as its default; a per-pixel mapped prop disables the skip. Give the prop `recompile: crosses(value)` so the skip switches on and off.","examples":["props: {intensity: {default: 1, recompile: crosses(1)}},\neffect: saturate(p('intensity')),\nidentityWhen: isValue('intensity', 1),"],"tips":[],"see":["slots.isZero","slots.allOf","slots.crosses"],"import":{"statement":"import {isValue} from 'shaders/std'","usage":"isValue"},"usedBy":["Exposure","Saturation"],"documented":true,"source":{"file":"std/slots.ts","line":81},"kind":"function","signature":"isValue(prop: string, value: unknown): IdentityRule","params":[{"name":"prop","type":"string","optional":false},{"name":"value","type":"unknown","optional":false}]},"slots.allOf":{"id":"slots.allOf","name":"allOf","qualifiedName":"allOf","category":"slots","module":"std/slots.ts","summary":"Skip the filter only while every rule holds, for a no-op that needs several props at rest.","description":"","examples":["identityWhen: allOf(isZero('brightness'), isZero('contrast'))"],"tips":[],"see":["slots.isZero","slots.isValue"],"import":{"statement":"import {allOf} from 'shaders/std'","usage":"allOf"},"usedBy":["BrightnessContrast"],"documented":true,"source":{"file":"std/slots.ts","line":94},"kind":"function","signature":"allOf(...rules: IdentityRule[]): IdentityRule","params":[{"name":"...rules","type":"IdentityRule[]","optional":true}]},"slots.identityWhenever":{"id":"slots.identityWhenever","name":"identityWhenever","qualifiedName":"identityWhenever","category":"slots","module":"std/slots.ts","summary":"Skip the filter when your own test of the props' values says so.","description":"List in `props` every prop the test reads: a per-pixel mapped prop among them disables the skip, and the engine needs the list to know which.","examples":["identityWhen: identityWhenever(['amount', 'mode'], (v) => v.amount === 0 || v.mode === 'off')"],"tips":[],"see":["slots.isZero","slots.isValue","slots.allOf"],"import":{"statement":"import {identityWhenever} from 'shaders/std'","usage":"identityWhenever"},"usedBy":[],"documented":true,"source":{"file":"std/slots.ts","line":110},"kind":"function","signature":"identityWhenever(props: string[], when: (values: Record<string, unknown>) => boolean): IdentityRule","params":[{"name":"props","type":"string[]","optional":false},{"name":"when","type":"(values: Record<string, unknown>) => boolean","optional":false}]},"invoke.ctx.uv":{"id":"invoke.ctx.uv","name":"uv","qualifiedName":"ctx.uv","category":"invoke","module":"std/invoke.ts","summary":"The pixel's coordinate: 0–1 across the canvas, y down.","description":"","examples":[],"tips":[],"see":[],"import":{"statement":"import {ctx} from 'shaders/std'","usage":"ctx.uv"},"usedBy":[],"documented":true,"source":{"file":"std/invoke.ts","line":48},"kind":"constant","signature":"ctx.uv: CtxToken","params":[]},"invoke.ctx.aspect":{"id":"invoke.ctx.aspect","name":"aspect","qualifiedName":"ctx.aspect","category":"invoke","module":"std/invoke.ts","summary":"The canvas's width divided by its height.","description":"","examples":[],"tips":[],"see":[],"import":{"statement":"import {ctx} from 'shaders/std'","usage":"ctx.aspect"},"usedBy":[],"documented":true,"source":{"file":"std/invoke.ts","line":50},"kind":"constant","signature":"ctx.aspect: CtxToken","params":[]},"invoke.ctx.time":{"id":"invoke.ctx.time","name":"time","qualifiedName":"ctx.time","category":"invoke","module":"std/invoke.ts","summary":"The global clock in seconds.","description":"Never pauses; for the layer's own speed-scaled clock declare `animatedTime`.","examples":[],"tips":[],"see":[],"import":{"statement":"import {ctx} from 'shaders/std'","usage":"ctx.time"},"usedBy":[],"documented":true,"source":{"file":"std/invoke.ts","line":52},"kind":"constant","signature":"ctx.time: CtxToken","params":[]},"invoke.ctx.viewportSize":{"id":"invoke.ctx.viewportSize","name":"viewportSize","qualifiedName":"ctx.viewportSize","category":"invoke","module":"std/invoke.ts","summary":"The frame size in device pixels, as a `vec2f`.","description":"","examples":[],"tips":[],"see":[],"import":{"statement":"import {ctx} from 'shaders/std'","usage":"ctx.viewportSize"},"usedBy":[],"documented":true,"source":{"file":"std/invoke.ts","line":54},"kind":"constant","signature":"ctx.viewportSize: CtxToken","params":[]},"invoke.ctx.logicalViewportSize":{"id":"invoke.ctx.logicalViewportSize","name":"logicalViewportSize","qualifiedName":"ctx.logicalViewportSize","category":"invoke","module":"std/invoke.ts","summary":"The frame size in CSS pixels, as a `vec2f`, for feature counts that should not change with screen density.","description":"","examples":[],"tips":[],"see":[],"import":{"statement":"import {ctx} from 'shaders/std'","usage":"ctx.logicalViewportSize"},"usedBy":[],"documented":true,"source":{"file":"std/invoke.ts","line":56},"kind":"constant","signature":"ctx.logicalViewportSize: CtxToken","params":[]},"invoke.ctx.pointer":{"id":"invoke.ctx.pointer","name":"pointer","qualifiedName":"ctx.pointer","category":"invoke","module":"std/invoke.ts","summary":"The mouse position in uv (0–1, y down).","description":"","examples":[],"tips":[],"see":[],"import":{"statement":"import {ctx} from 'shaders/std'","usage":"ctx.pointer"},"usedBy":[],"documented":true,"source":{"file":"std/invoke.ts","line":58},"kind":"constant","signature":"ctx.pointer: CtxToken","params":[]},"invoke.ctx":{"id":"invoke.ctx","name":"ctx","qualifiedName":"ctx","category":"invoke","module":"std/invoke.ts","summary":"The context values as tokens: `ctx.uv`, `ctx.time`, `ctx.aspect`, `ctx.pointer`, `ctx.viewportSize`, `ctx.logicalViewportSize`.","description":"Pass one where a word takes a value, or bind it under another name in a `wgsl` body's `inputs`. `uv` is 0–1 across the canvas with y down. `time` is the global clock in seconds and never pauses; for the layer's own speed-scaled clock declare `animatedTime` and read `time` in a body or `animatedTime(params)` in a builder. `aspect` is width / height. `pointer` is the mouse in uv. `viewportSize` is the frame in device pixels, `logicalViewportSize` in CSS pixels.","examples":["paint: wgsl({inputs: {t: ctx.time, px: ctx.viewportSize}, body: 'return vec4f(fract(uv * px / 40.0), sin(t) * 0.5 + 0.5, 1.0);'})"],"tips":["A warp's `map:` has no context; a `ctx` token there throws when the shader is built."],"see":["values.p","invoke.paintFrame","define.wgsl"],"import":{"statement":"import {ctx} from 'shaders/std'","usage":"ctx"},"usedBy":[],"documented":true,"source":{"file":"std/invoke.ts","line":46},"kind":"object","signature":"ctx: { uv, aspect, time, viewportSize, logicalViewportSize, pointer }","members":["invoke.ctx.uv","invoke.ctx.aspect","invoke.ctx.time","invoke.ctx.viewportSize","invoke.ctx.logicalViewportSize","invoke.ctx.pointer"],"params":[]},"invoke.paintFrame":{"id":"invoke.paintFrame","name":"paintFrame","qualifiedName":"paintFrame","category":"invoke","module":"std/invoke.ts","summary":"The coordinate and frame size a hand-written `paint:` builder should draw in.","description":"Returns `uv` and `viewport` as GPU values. Inside a distortion the uv is the distorted one, and inside a sized box the viewport is the box, so a generator built on these behaves like the library's own.","examples":["paint: (params) => {\n  const {uv} = paintFrame(params)\n  const center = uniformOf(p('center'), params)\n  const d = length(sub(uv, vec2(center.member('x'), sub(1, center.member('y')))))\n  return vec4(splat3(smoothstep(0, 0.5, d)), 1)\n}"],"tips":[],"see":["invoke.ctx","invoke.uniformOf"],"import":{"statement":"import {paintFrame} from 'shaders/std'","usage":"paintFrame"},"usedBy":[],"documented":true,"source":{"file":"std/invoke.ts","line":87},"kind":"function","signature":"paintFrame(params: GpuFragmentParams): {uv: Expr; viewport: Expr}","params":[{"name":"params","type":"GpuFragmentParams","optional":false}]},"invoke.uniformOf":{"id":"invoke.uniformOf","name":"uniformOf","qualifiedName":"uniformOf","category":"invoke","module":"std/invoke.ts","summary":"The live GPU value of a prop, for a hand-written builder: `uniformOf(p('radius'), params)`.","description":"Colors come back as `vec4f` in linear RGB with alpha, positions as `vec2f`, numbers as `f32`. Read a part with `.member('rgb')` or `.member('x')`. Throws when the name is not one of the definition's props.","examples":["paint: (params) => {\n  const color = uniformOf(p('color'), params)\n  const gain = mul(uniformOf(p('intensity'), params), 0.5)\n  return vec4(mul(color.member('rgb'), gain), color.member('a'))\n}"],"tips":["A position prop's y is stored flipped: use `sub(1, center.member('y'))` to compare it with `uv`."],"see":["values.p","invoke.resolveScalar","invoke.resolveArg","invoke.paintFrame"],"import":{"statement":"import {uniformOf} from 'shaders/std'","usage":"uniformOf"},"usedBy":["Aurora","Blob","FlowingGradient","GlassTiles","Marble","MeshGradient","Prism","Satin","StudioBackground","Waveform"],"documented":true,"source":{"file":"std/invoke.ts","line":113},"kind":"function","signature":"uniformOf(ref: PropRef, params: {uniforms: Record<string, Expr>}): Expr","params":[{"name":"ref","type":"PropRef","optional":false},{"name":"params","type":"{uniforms: Record<string, Expr>}","optional":false}]},"invoke.resolveScalar":{"id":"invoke.resolveScalar","name":"resolveScalar","qualifiedName":"resolveScalar","category":"invoke","module":"std/invoke.ts","summary":"A `Scalar` or prop ref as a GPU value, for a hand-written builder.","description":"Products (`.times()`), radial masks and signals are built out here. A radial mask needs a `paint:` or `effect:` host; it throws in a warp's `map:`.","examples":["const amount = resolveScalar(radialMask({center: p('center'), radius: p('radius'), falloff: p('falloff')}).times(p('intensity')), params)"],"tips":[],"see":["invoke.uniformOf","invoke.resolveArg","values.Scalar"],"import":{"statement":"import {resolveScalar} from 'shaders/std'","usage":"resolveScalar"},"usedBy":["Blob"],"documented":true,"source":{"file":"std/invoke.ts","line":131},"kind":"function","signature":"resolveScalar(input: Scalar | ScalarSource, params: SlotParams): Expr","params":[{"name":"input","type":"Scalar | ScalarSource","optional":false},{"name":"params","type":"SlotParams","optional":false}]},"invoke.resolveArg":{"id":"invoke.resolveArg","name":"resolveArg","qualifiedName":"resolveArg","category":"invoke","module":"std/invoke.ts","summary":"Any input value a word accepts (a prop ref, a `Scalar`, a context token, a number) as a GPU value inside a filter's builder.","description":"","examples":["effect: pointwise({build: (params) => mix(params.childNode, vec4(0, 0, 0, 1), resolveArg(p('amount'), params))})"],"tips":[],"see":["invoke.resolveArgIn","invoke.uniformOf","invoke.resolveScalar"],"import":{"statement":"import {resolveArg} from 'shaders/std'","usage":"resolveArg"},"usedBy":["LightLeak"],"documented":true,"source":{"file":"std/invoke.ts","line":158},"kind":"function","signature":"resolveArg(spec: ArgSpec, params: FilterParams): Expr","params":[{"name":"spec","type":"ArgSpec","optional":false},{"name":"params","type":"FilterParams","optional":false}]},"invoke.resolveArgIn":{"id":"invoke.resolveArgIn","name":"resolveArgIn","qualifiedName":"resolveArgIn","category":"invoke","module":"std/invoke.ts","summary":"`resolveArg` for any host, including a warp's `map:` factory.","description":"A warp has no context, so a `ctx` token or a radial mask throws there; props, scalars and numbers resolve everywhere.","examples":["map: (params) => ({map: (uv) => add(uv, mul(resolveArgIn(p('offset'), params), 0.1))})"],"tips":[],"see":["invoke.resolveArg"],"import":{"statement":"import {resolveArgIn} from 'shaders/std'","usage":"resolveArgIn"},"usedBy":[],"documented":true,"source":{"file":"std/invoke.ts","line":174},"kind":"function","signature":"resolveArgIn(spec: ArgSpec, params: SlotParams): Expr","params":[{"name":"spec","type":"ArgSpec","optional":false},{"name":"params","type":"SlotParams","optional":false}]},"invoke.pointwiseOp":{"id":"invoke.pointwiseOp","name":"pointwiseOp","qualifiedName":"pointwiseOp","category":"invoke","module":"std/invoke.ts","summary":"Wrap a compiled GPU function as a pointwise filter effect: the child's color goes in first, then `args`.","description":"This is how the library's color words are made. `fn` takes `(color: vec4f, ...args)` and returns the new color; `hint` names it in the generated code; `args` are the values that follow the color, given as prop refs, scalars, context tokens or numbers. `extra.compose` post-processes the result and `extra.setup` runs once per build.","examples":["export function saturate(intensity: ArgSpec): PointwiseEffect {\n  return pointwiseOp(colorOps.saturate, 'saturate', [intensity])\n}"],"tips":[],"see":["invoke.resolveArg","values.p"],"import":{"statement":"import {pointwiseOp} from 'shaders/std'","usage":"pointwiseOp"},"usedBy":[],"documented":true,"source":{"file":"std/invoke.ts","line":207},"kind":"function","signature":"pointwiseOp(fn: unknown, hint: string, args: ArgSpec[], extra?: {compose?: PointwiseEffect['compose']; setup?: PointwiseEffect['setup']}): PointwiseEffect","params":[{"name":"fn","type":"unknown","optional":false},{"name":"hint","type":"string","optional":false},{"name":"args","type":"ArgSpec[]","optional":false},{"name":"extra","type":"{compose?: PointwiseEffect['compose']; setup?: PointwiseEffect['setup']}","optional":true}]},"lists.listOf":{"id":"lists.listOf","name":"listOf","qualifiedName":"listOf","category":"lists","module":"std/lists.ts","summary":"Read a list prop inside a hand-written builder: its live item count and each item's fields.","description":"`listOf(params, 'lights')` gives `count` and `at(i)`, whose `.position('position')`, `.color('color')` and `.number('intensity')` read item `i` by field name. Throws when `prop` is not a list prop or a field name is not in its item spec. Loop with `accumulate`.","examples":["const lights = listOf(params, 'lights')\nconst rgb = accumulate(lights, (i) => mul(lights.at(i).color('color').member('rgb'), lights.at(i).number('intensity')), {zero: 'vec3f'})"],"tips":["A position field's y is stored flipped; use `sub(1, pos.member('y'))` to compare it with `uv`."],"see":["lists.accumulate"],"import":{"statement":"import {listOf} from 'shaders/std'","usage":"listOf"},"usedBy":["Irradiance"],"documented":true,"source":{"file":"std/lists.ts","line":58},"kind":"function","signature":"listOf(params: {uniforms: Record<string, Expr>}, prop: string): ListRef","params":[{"name":"params","type":"{uniforms: Record<string, Expr>}","optional":false},{"name":"prop","type":"string","optional":false}]},"lists.accumulate":{"id":"lists.accumulate","name":"accumulate","qualifiedName":"accumulate","category":"lists","module":"std/lists.ts","summary":"Add up a per-item value over every item of a list, in a real GPU loop over the live count.","description":"`body(i)` returns the value for item `i`. `zero` picks the sum's type: `'f32'` (default) or `'vec3f'` for a color. Anything the body reads that was computed outside the loop (a normal, a surface point) goes in `deps`, so it is emitted once before the loop instead of inside it. `hint` names the sum in the generated code.","examples":["const lights = listOf(params, 'lights')\nconst rgb = accumulate(lights, (i) => {\n  const light = lights.at(i)\n  const pos = light.position('position')\n  const d = length(sub(uv, vec2(pos.member('x'), sub(1, pos.member('y')))))\n  return mul(light.color('color').member('rgb'), div(light.number('intensity'), add(1, mul(d, d))))\n}, {zero: 'vec3f', deps: [uv], hint: 'lightSum'})"],"tips":[],"see":["lists.listOf"],"import":{"statement":"import {accumulate} from 'shaders/std'","usage":"accumulate"},"usedBy":["Irradiance"],"documented":true,"source":{"file":"std/lists.ts","line":98},"kind":"function","signature":"accumulate(list: ListRef, body: (i: Expr) => Expr, opts: {zero?: 'f32' | 'vec3f'; deps?: Expr[]; hint?: string} = {}): Expr","params":[{"name":"list","type":"ListRef","optional":false},{"name":"body","type":"(i: Expr) => Expr","optional":false},{"name":"opts","type":"{zero?: 'f32' | 'vec3f'; deps?: Expr[]; hint?: string}","optional":true,"defaultValue":"{}"}]},"paint.colorLadder3":{"id":"paint.colorLadder3","name":"colorLadder3","qualifiedName":"colorLadder3","category":"paint","module":"std/paint/fields.ts","summary":"A three-color blend: `a` to `b` by the first value, then on to `c` by the second.","description":"Returns a function you call with two 0–1 values and the params. Both mixes happen in the color space named by the `space` prop (mark it `compileTime`).","examples":["paint: (params) => colorLadder3({a: p('colorA'), b: p('colorB'), c: p('colorC'), space: p('colorSpace')})(t1, t2, params)"],"tips":[],"see":["paint.pair","paint.quadBlend"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.colorLadder3"},"usedBy":["Aurora","Marble"],"documented":true,"source":{"file":"std/paint/fields.ts","line":128},"kind":"function","signature":"colorLadder3(slots: {a: PropRef; b: PropRef; c: PropRef; space: PropRef}): (t1: Expr, t2: Expr, params: GpuFragmentParams) => Expr","params":[{"name":"slots","type":"{a: PropRef; b: PropRef; c: PropRef; space: PropRef}","optional":false}]},"paint.pair":{"id":"paint.pair","name":"pair","qualifiedName":"pair","category":"paint","module":"std/paint/fields.ts","summary":"A palette of two color props, mixed in the color space named by the `space` prop.","description":"Mark the `space` prop `compileTime`. Field value 0 gives `a`, 1 gives `b`.","examples":["paint: rampOver(dist.radial({center: p('center'), radius: p('radius'), aspect: 1, skew: 0}), pair(p('colorA'), p('colorB'), p('colorSpace')))"],"tips":[],"see":["paint.stops","paint.standardPalette"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.pair"},"usedBy":["Spiral"],"documented":true,"source":{"file":"std/paint/fields.ts","line":147},"kind":"function","signature":"pair(a: PropRef, b: PropRef, space: PropRef): Palette","params":[{"name":"a","type":"PropRef","optional":false},{"name":"b","type":"PropRef","optional":false},{"name":"space","type":"PropRef","optional":false}]},"paint.stops":{"id":"paint.stops","name":"stops","qualifiedName":"stops","category":"paint","module":"std/paint/fields.ts","summary":"The standard multi-stop palette, read from the `stops`, `colorA` and `colorB` props.","description":"Declare `stops: colorStopsPropConfig()` plus `colorA` and `colorB` on the definition. When the user has set two or more stops the ramp uses them, otherwise it mixes `colorA` to `colorB`. Both mix in the color space named by the `space` prop (mark it `compileTime`).","examples":["paint: rampOver(dist.linear({from: p('start'), to: p('end'), angle: p('angle')}), stops(p('colorSpace')), {edges: p('edges')})"],"tips":["The prop names are fixed: `stops`, `colorA`, `colorB`. Use `pair` when yours differ."],"see":["paint.pair","paint.standardPalette"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.stops"},"usedBy":["Blob","BlockNoise","BlueNoise","ConicGradient","CurlNoise","DiamondGradient","ErosionNoise","GaborNoise","LinearGradient","Obsidian","PerlinNoise","Plasma","RadialGradient","SimplexNoise","Swirl","Voronoi","Waveform","WaveletNoise"],"documented":true,"source":{"file":"std/paint/fields.ts","line":169},"kind":"function","signature":"stops(space: PropRef): Palette","params":[{"name":"space","type":"PropRef","optional":false}]},"paint.rampOver":{"id":"paint.rampOver","name":"rampOver","qualifiedName":"rampOver","category":"paint","module":"std/paint/fields.ts","summary":"A paint that colors each pixel by looking its field value up in a palette.","description":"Pass an `edges` prop (values `stretch`, `transparent`, `mirror`, `wrap`, marked `compileTime`) to choose what happens where the field runs past 0–1: hold the end colors, fade to transparent, bounce back, or repeat. Without it the value goes to the palette as is, which is right for fields that already stay in range such as `rings` or `tone`.","examples":["paint: rampOver(dist.linear({from: p('start'), to: p('end'), angle: p('angle')}), stops(p('colorSpace')), {edges: p('edges')})"],"tips":["This is the whole recipe for a gradient: a `dist.*` field, a palette, done."],"see":["paint.dist","paint.pair","paint.stops","paint.rings","paint.tiles"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.rampOver"},"usedBy":["BlockNoise","BlueNoise","ConicGradient","CurlNoise","DiamondGradient","ErosionNoise","GaborNoise","LinearGradient","PerlinNoise","Plasma","RadialGradient","Scratches","SimplexNoise","Spiral","Swirl","Voronoi","WaveletNoise"],"documented":true,"source":{"file":"std/paint/fields.ts","line":214},"kind":"function","signature":"rampOver(field: Field, palette: Palette, opts?: {edges?: PropRef}): Paint","params":[{"name":"field","type":"Field","optional":false},{"name":"palette","type":"Palette","optional":false},{"name":"opts","type":"{edges?: PropRef}","optional":true}]},"paint.dist.linear":{"id":"paint.dist.linear","name":"linear","qualifiedName":"dist.linear","category":"paint","module":"std/paint/fields.ts","summary":"A straight gradient from one point to another.","description":"0 at `from`, 1 at `to`. `angle` in degrees turns the whole gradient about the midpoint.","examples":["paint: rampOver(dist.linear({from: p('start'), to: p('end'), angle: p('angle')}), stops(p('colorSpace')), {edges: p('edges')})"],"tips":[],"see":["paint.rampOver"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.dist.linear"},"usedBy":["LinearGradient"],"documented":true,"source":{"file":"std/paint/fields.ts","line":263},"kind":"function","signature":"dist.linear(slots: {from: PropRef; to: PropRef; angle: FieldScalar}): Field","params":[{"name":"slots","type":"{from: PropRef; to: PropRef; angle: FieldScalar}","optional":false}]},"paint.dist.radial":{"id":"paint.dist.radial","name":"radial","qualifiedName":"dist.radial","category":"paint","module":"std/paint/fields.ts","summary":"A round or elliptical gradient spreading out from a center.","description":"0 at `center`, 1 at `radius` (a fraction of the canvas height). `aspect` 1 is a circle, larger values squeeze it horizontally. `skew` in degrees tilts the ellipse.","examples":["paint: rampOver(rings(dist.radial({center: p('center'), radius: p('radius'), aspect: p('aspect'), skew: p('skewAngle')}), p('repeat')), stops(p('colorSpace')))"],"tips":[],"see":["paint.rings"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.dist.radial"},"usedBy":["RadialGradient"],"documented":true,"source":{"file":"std/paint/fields.ts","line":284},"kind":"function","signature":"dist.radial(slots: {center: PropRef; radius: FieldScalar; aspect: FieldScalar; skew: FieldScalar}): Field","params":[{"name":"slots","type":"{center: PropRef; radius: FieldScalar; aspect: FieldScalar; skew: FieldScalar}","optional":false}]},"paint.dist.conic":{"id":"paint.dist.conic","name":"conic","qualifiedName":"dist.conic","category":"paint","module":"std/paint/fields.ts","summary":"A sweep around a center, like a clock hand: 0 to 1 once around.","description":"The sweep starts at the left of the center and runs counter-clockwise. `rotation` in degrees moves the start. Wrap it in `tiles` to repeat the sweep.","examples":["paint: rampOver(tiles(dist.conic({center: p('center'), rotation: p('rotation')}), p('repeat')), stops(p('colorSpace')))"],"tips":[],"see":["paint.tiles"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.dist.conic"},"usedBy":["ConicGradient"],"documented":true,"source":{"file":"std/paint/fields.ts","line":305},"kind":"function","signature":"dist.conic(slots: {center: PropRef; rotation: FieldScalar}): Field","params":[{"name":"slots","type":"{center: PropRef; rotation: FieldScalar}","optional":false}]},"paint.dist.diamond":{"id":"paint.dist.diamond","name":"diamond","qualifiedName":"dist.diamond","category":"paint","module":"std/paint/fields.ts","summary":"A diamond-shaped gradient spreading out from a center.","description":"0 at `center`, 1 at `size` (a fraction of the canvas height). `roundness` 0 is a sharp diamond and 1 is a square. `rotation` in degrees tilts it.","examples":["paint: rampOver(rings(dist.diamond({center: p('center'), size: p('size'), rotation: p('rotation'), roundness: p('roundness')}), p('repeat')), stops(p('colorSpace')))"],"tips":[],"see":["paint.rings"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.dist.diamond"},"usedBy":["DiamondGradient"],"documented":true,"source":{"file":"std/paint/fields.ts","line":326},"kind":"function","signature":"dist.diamond(slots: {center: PropRef; size: FieldScalar; rotation: FieldScalar; roundness: FieldScalar}): Field","params":[{"name":"slots","type":"{center: PropRef; size: FieldScalar; rotation: FieldScalar; roundness: FieldScalar}","optional":false}]},"paint.dist.spiral":{"id":"paint.dist.spiral","name":"spiral","qualifiedName":"dist.spiral","category":"paint","module":"std/paint/fields.ts","summary":"A turning spiral stripe: 0 on the background, 1 on the stroke.","description":"It spins on the layer's clock, so declare `animatedTime: {speed: 'speed'}`. `scale` sets how many turns fit, `width` (0–2) the stroke thickness, `falloff` (0–1) thins the stroke away from the center, `softness` (0–1) blurs its edge. Unlike the other distance fields this one is already 0–1, so pair it with a palette of background and stroke colors.","examples":["paint: rampOver(dist.spiral({center: p('center'), scale: p('scale'), width: p('strokeWidth'), falloff: p('strokeFalloff'), softness: p('softness')}), pair(p('background'), p('stroke'), p('colorSpace')))"],"tips":[],"see":[],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.dist.spiral"},"usedBy":["Spiral"],"documented":true,"source":{"file":"std/paint/fields.ts","line":348},"kind":"function","signature":"dist.spiral(slots: {center: PropRef; scale: FieldScalar; width: FieldScalar; falloff: FieldScalar; softness: FieldScalar}): Field","params":[{"name":"slots","type":"{center: PropRef; scale: FieldScalar; width: FieldScalar; falloff: FieldScalar; softness: FieldScalar}","optional":false}]},"paint.dist":{"id":"paint.dist","name":"dist","qualifiedName":"dist","category":"paint","module":"std/paint/fields.ts","summary":"Distance fields: the shape of a gradient, as a number that grows away from a line or point.","description":"Each takes position props made with `transformPosition` and returns 0 at the start and 1 at the end, unbounded beyond. Feed one to `rampOver`.","examples":[],"tips":[],"see":["paint.rampOver","paint.rings","paint.tiles"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.dist"},"usedBy":["ConicGradient","DiamondGradient","LinearGradient","RadialGradient","Spiral"],"documented":true,"source":{"file":"std/paint/fields.ts","line":251},"kind":"object","signature":"dist: { linear, radial, conic, diamond, spiral }","members":["paint.dist.linear","paint.dist.radial","paint.dist.conic","paint.dist.diamond","paint.dist.spiral"],"params":[]},"paint.rings":{"id":"paint.rings","name":"rings","qualifiedName":"rings","category":"paint","module":"std/paint/fields.ts","summary":"Repeats a field as concentric bands.","description":"`count` above 1 restarts the field every 1/count, so a radial gradient becomes rings. At 1 or below the field is clamped to 0–1, and values under 1 stretch the gradient further out.","examples":["paint: rampOver(rings(dist.radial({center: p('center'), radius: p('radius'), aspect: 1, skew: 0}), p('repeat')), stops(p('colorSpace')))"],"tips":["Each band restarts at the first color rather than bouncing back. For a mirrored repeat, pass `edges` to `rampOver` instead."],"see":["paint.tiles","paint.rampOver"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.rings"},"usedBy":["DiamondGradient","RadialGradient"],"documented":true,"source":{"file":"std/paint/fields.ts","line":382},"kind":"function","signature":"rings(field: Field, count: FieldScalar): Field","params":[{"name":"field","type":"Field","optional":false},{"name":"count","type":"FieldScalar","optional":false}]},"paint.tiles":{"id":"paint.tiles","name":"tiles","qualifiedName":"tiles","category":"paint","module":"std/paint/fields.ts","summary":"Repeats a field `count` times, always wrapping.","description":"Made for sweeps: `tiles(dist.conic(...), 4)` gives four sectors. Any count works, including fractions.","examples":["paint: rampOver(tiles(dist.conic({center: p('center'), rotation: p('rotation')}), p('repeat')), stops(p('colorSpace')))"],"tips":[],"see":["paint.rings"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.tiles"},"usedBy":["ConicGradient"],"documented":true,"source":{"file":"std/paint/fields.ts","line":399},"kind":"function","signature":"tiles(field: Field, count: FieldScalar): Field","params":[{"name":"field","type":"Field","optional":false},{"name":"count","type":"FieldScalar","optional":false}]},"paint.tone":{"id":"paint.tone","name":"tone","qualifiedName":"tone","category":"paint","module":"std/paint/fields.ts","summary":"Reshapes a 0–1 field with glow, contrast and balance controls, then flips it.","description":"`glow` is a spread control: 5 leaves the field alone, lower values pull the bright parts into tight cores, higher values spread them into a wash. `contrast` scales around the middle, 1 is neutral. `balance` is 0–100 with 50 neutral. `invert` is always true: the result is flipped so the palette's first color lands where the field is high.","examples":["paint: rampOver(tone(scaledVolume(noiseField('mx3'), {scale: p('scale')}), {glow: p('intensity'), contrast: p('contrast'), balance: p('balance'), invert: true}), stops(p('colorSpace')))"],"tips":["Raising `balance` pushes more of the canvas toward the first color."],"see":["paint.threshold","paint.noiseField"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.tone"},"usedBy":["Plasma"],"documented":true,"source":{"file":"std/paint/fields.ts","line":418},"kind":"function","signature":"tone(field: Field, opts: {glow: FieldScalar; contrast: FieldScalar; balance: FieldScalar; invert: true}): Field","params":[{"name":"field","type":"Field","optional":false},{"name":"opts","type":"{glow: FieldScalar; contrast: FieldScalar; balance: FieldScalar; invert: true}","optional":false}]},"paint.threshold":{"id":"paint.threshold","name":"threshold","qualifiedName":"threshold","category":"paint","module":"std/paint/fields.ts","summary":"Turns a signed field (−1 to 1) into a soft 0–1 mask.","description":"The field is first mapped to 0–1 and shifted by `bias`, then eased from 0 at `low` to 1 at `high`. Bring `low` and `high` together for a hard edge.","examples":["paint: rampOver(threshold(flowField({detail: p('detail')}), {low: 0.3, high: 0.7, bias: {base: p('blend'), add: -50, mul: 0.006}}), stops(p('colorSpace')))"],"tips":[],"see":["paint.flowField","paint.tone"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.threshold"},"usedBy":["Swirl"],"documented":true,"source":{"file":"std/paint/fields.ts","line":438},"kind":"function","signature":"threshold(field: Field, opts: {low: number; high: number; bias?: FieldScalar}): Field","params":[{"name":"field","type":"Field","optional":false},{"name":"opts","type":"{low: number; high: number; bias?: FieldScalar}","optional":false}]},"paint.share":{"id":"paint.share","name":"share","qualifiedName":"share","category":"paint","module":"std/paint/fields.ts","summary":"Evaluates a field once and lets several words read the same result.","description":"Wrap a field in `share` when it feeds two places, such as a `threshold` and a `pulse`.","examples":["const pattern = share(flowField({detail: p('detail')}))\n// then: threshold(pattern, ...) and pulse(pattern, ...)"],"tips":["One result per layer, whatever coordinate it is asked for. Do not use it to sample the same field at two offsets."],"see":["paint.flowField","paint.pulse"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.share"},"usedBy":["Swirl","Voronoi"],"documented":true,"source":{"file":"std/paint/fields.ts","line":460},"kind":"function","signature":"share(field: Field): Field","params":[{"name":"field","type":"Field","optional":false}]},"paint.noiseField":{"id":"paint.noiseField","name":"noiseField","qualifiedName":"noiseField","category":"paint","module":"std/paint/fields.ts","summary":"A field of smooth noise in the chosen flavor.","description":"Noise needs a 3D coordinate, so put it inside `scaledVolume` (and `warped` for motion).","examples":["paint: rampOver(scaledVolume(noiseField('mx3'), {scale: p('scale')}), stops(p('colorSpace')))"],"tips":[],"see":["paint.scaledVolume","paint.warped","paint.tone"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.noiseField"},"usedBy":["BlockNoise","PerlinNoise","Plasma","SimplexNoise"],"documented":true,"source":{"file":"std/paint/fields.ts","line":506},"kind":"function","signature":"noiseField(basis: NoiseBasis): Field","params":[{"name":"basis","type":"NoiseBasis","optional":false}]},"paint.warped":{"id":"paint.warped","name":"warped","qualifiedName":"warped","category":"paint","module":"std/paint/fields.ts","summary":"Makes a field flow like liquid by bending its coordinate with moving noise.","description":"`amount` sets how far the coordinate is pushed (0 is off), `amountScale` multiplies it so a 0–1 slider can reach further. The motion runs on the layer's clock times `timeScale`, so declare `animatedTime: {speed: 'speed'}`. Needs a 3D coordinate: put it inside `scaledVolume`.","examples":["scaledVolume(warped(noiseField('mx3'), {amount: p('warp'), amountScale: 4, timeScale: 0.125}), {scale: p('scale')})"],"tips":[],"see":["paint.noiseField","paint.scaledVolume","paint.warpedPoint"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.warped"},"usedBy":["Plasma"],"documented":true,"source":{"file":"std/paint/fields.ts","line":524},"kind":"function","signature":"warped(field: Field, opts: {amount: FieldScalar; amountScale?: number; timeScale?: number}): Field","params":[{"name":"field","type":"Field","optional":false},{"name":"opts","type":"{amount: FieldScalar; amountScale?: number; timeScale?: number}","optional":false}]},"paint.scaledVolume":{"id":"paint.scaledVolume","name":"scaledVolume","qualifiedName":"scaledVolume","category":"paint","module":"std/paint/fields.ts","summary":"Gives a noise field a zoomable, aspect-correct coordinate.","description":"Turns the pixel's uv into a 3D point so `noiseField` and `warped` can read it. `scale` zooms exponentially: 1 is neutral and each step up magnifies about 2.7 times. A 0–4 slider works well.","examples":["paint: rampOver(scaledVolume(noiseField('perlin'), {scale: p('scale')}), stops(p('colorSpace')))"],"tips":[],"see":["paint.noiseField","paint.warped"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.scaledVolume"},"usedBy":["Plasma"],"documented":true,"source":{"file":"std/paint/fields.ts","line":550},"kind":"function","signature":"scaledVolume(field: Field, opts: {scale: FieldScalar}): Field","params":[{"name":"field","type":"Field","optional":false},{"name":"opts","type":"{scale: FieldScalar}","optional":false}]},"paint.flowField":{"id":"paint.flowField","name":"flowField","qualifiedName":"flowField","category":"paint","module":"std/paint/fields.ts","summary":"A swirling, animated wave pattern from −1 to 1.","description":"`detail` sets how fine the swirls are. It moves on the layer's clock, so declare `animatedTime: {speed: 'speed'}`. Feed it to `threshold` for color bands or to `pulse` for a shimmer.","examples":["paint: rampOver(threshold(flowField({detail: p('detail')}), {low: 0.3, high: 0.7}), stops(p('colorSpace')))"],"tips":[],"see":["paint.threshold","paint.pulse","paint.share"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.flowField"},"usedBy":["Swirl"],"documented":true,"source":{"file":"std/paint/fields.ts","line":573},"kind":"function","signature":"flowField(slots: {detail: FieldScalar}): Field","params":[{"name":"slots","type":"{detail: FieldScalar}","optional":false}]},"paint.pulse":{"id":"paint.pulse","name":"pulse","qualifiedName":"pulse","category":"paint","module":"std/paint/fields.ts","summary":"A gentle brightness pulse that follows a pattern field, hovering around 1.","description":"The result swings between 1 − `depth` and 1 + `depth`, so `depth` around 0.02 is subtle. `speed` is how fast it breathes, `span` how much the pattern staggers the phase. Use it as the gain in `shimmered`.","examples":["paint: shimmered(rampOver(threshold(pattern, {low: 0.3, high: 0.7}), stops(p('colorSpace'))), pulse(pattern, {speed: 2.5, span: 8, depth: 0.015}))"],"tips":[],"see":["paint.shimmered","paint.flowField"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.pulse"},"usedBy":["Swirl"],"documented":true,"source":{"file":"std/paint/fields.ts","line":592},"kind":"function","signature":"pulse(field: Field, opts: {speed: number; span: number; depth: number}): Field","params":[{"name":"field","type":"Field","optional":false},{"name":"opts","type":"{speed: number; span: number; depth: number}","optional":false}]},"paint.hueWheel":{"id":"paint.hueWheel","name":"hueWheel","qualifiedName":"hueWheel","category":"paint","module":"std/paint/fields.ts","summary":"A rainbow color for a hue value: 0 is red, cycling through the spectrum and back every 1.","description":"Takes any number and wraps it. Returns an rgb color with no alpha.","examples":["const rgb = hueWheel(math.add(math.mul(angle, 0.42), animatedTime(params)))"],"tips":[],"see":[],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.hueWheel"},"usedBy":["Prism"],"documented":true,"source":{"file":"std/paint/fields.ts","line":609},"kind":"function","signature":"hueWheel(h: Expr): Expr","params":[{"name":"h","type":"Expr","optional":false}]},"paint.shimmered":{"id":"paint.shimmered","name":"shimmered","qualifiedName":"shimmered","category":"paint","module":"std/paint/fields.ts","summary":"Multiplies a paint's color and alpha by a gain field, for a slow shimmer over a pattern.","description":"The gain is read at the raw canvas uv. Build it with `pulse`.","examples":["paint: shimmered(rampOver(field, stops(p('colorSpace'))), pulse(field, {speed: 2.5, span: 8, depth: 0.015}))"],"tips":[],"see":["paint.pulse"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.shimmered"},"usedBy":["Swirl"],"documented":true,"source":{"file":"std/paint/fields.ts","line":630},"kind":"function","signature":"shimmered(paint: Paint, gain: Field): Paint","params":[{"name":"paint","type":"Paint","optional":false},{"name":"gain","type":"Field","optional":false}]},"paint.scatterField":{"id":"paint.scatterField","name":"scatterField","qualifiedName":"scatterField","category":"paint","module":"std/paint/fields.ts","summary":"A mesh-gradient field: up to 8 drifting points scattered across the canvas, blended by nearness.","description":"Each point carries a fixed 0–1 value and the result is their distance-weighted average, so a palette lookup gives soft patches of color. `at` is an aspect-corrected coordinate (x runs 0 to the aspect ratio, y 0–1). `count` is 1–8, `seed` reshuffles the layout, `drift` (0–1) sets how far points wander on `time`, `softness` sharpens the patches as it rises. Pass `anchors` from `scatteredAnchors` to skip recomputing the points per pixel.","examples":["const t = scatterField({at: uv, count: uniformOf(p('count'), params), seed, drift: uniformOf(p('drift'), params), aspect, time, softness: 3})\nreturn standardPalette(t, params)"],"tips":[],"see":["paint.scatteredAnchors","paint.wrapRamp","paint.standardPalette"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.scatterField"},"usedBy":["MeshGradient"],"documented":true,"source":{"file":"std/paint/fields.ts","line":652},"kind":"function","signature":"scatterField(opts: {\n    at: Expr\n    count: Expr | number\n    seed: Expr | number\n    drift: Expr | number\n    aspect: Expr | number\n    time: Expr | number\n    softness: Expr | number\n    /** The points precomputed each frame by `scatteredAnchors`. When given, `drift`, `aspect` and `time` are ignored here. */\n    anchors?: Expr\n}): Expr","params":[{"name":"opts","type":"{\n    at: Expr\n    count: Expr | number\n    seed: Expr | number\n    drift: Expr | number\n    aspect: Expr | number\n    time: Expr | number\n    softness: Expr | number\n    /** The points precomputed each frame by `scatteredAnchors`. When given, `drift`, `aspect` and `time` are ignored here. */\n    anchors?: Expr\n}","optional":false}]},"paint.scatteredAnchors":{"id":"paint.scatteredAnchors","name":"scatteredAnchors","qualifiedName":"scatteredAnchors","category":"paint","module":"std/paint/fields.ts","summary":"Computes the `scatterField` points once per frame instead of once per pixel.","description":"Declare a field on the definition, `extraFields: {meshAnchors: {schema: schema.arrayOf(schema.vec4f, 4), initial: new Array(16).fill(0)}}`, name it in `field`, and pass the result as `anchors` to `scatterField`. The points follow the layer's clock, so declare `animatedTime`.","examples":["const anchors = scatteredAnchors(params, {count: p('count'), seed: p('seed'), drift: p('drift'), field: 'meshAnchors'})\nconst t = scatterField({at: uv, count: uniformOf(p('count'), params), seed, drift: 0, aspect: 1, time: 0, softness: 3, anchors})"],"tips":[],"see":["paint.scatterField"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.scatteredAnchors"},"usedBy":["MeshGradient"],"documented":true,"source":{"file":"std/paint/fields.ts","line":691},"kind":"function","signature":"scatteredAnchors(params: GpuFragmentParams, slots: {count: PropRef; seed: PropRef; drift: PropRef; field: string}): Expr","params":[{"name":"params","type":"GpuFragmentParams","optional":false},{"name":"slots","type":"{count: PropRef; seed: PropRef; drift: PropRef; field: string}","optional":false}]},"paint.wrapRamp":{"id":"paint.wrapRamp","name":"wrapRamp","qualifiedName":"wrapRamp","category":"paint","module":"std/paint/fields.ts","summary":"Runs a palette value back and forth `cycles` times so a ramp repeats without a seam.","description":"At `cycles` 1 the value passes through unchanged. Where the bands get thinner than a pixel they fade to the palette's middle instead of flickering. Use it on the value before the palette lookup, in a `paint:` function.","examples":["return standardPalette(wrapRamp({t: field, cycles: math.add(1, math.mul(uniformOf(p('wrapping'), params), 6))}), params)"],"tips":["Not for use inside a `guarded` branch. Reach for `foldRamp` there."],"see":["paint.foldRamp","paint.scatterField"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.wrapRamp"},"usedBy":["MeshGradient"],"documented":true,"source":{"file":"std/paint/fields.ts","line":728},"kind":"function","signature":"wrapRamp(opts: {t: Expr; cycles: Expr | number}): Expr","params":[{"name":"opts","type":"{t: Expr; cycles: Expr | number}","optional":false}]},"paint.foldRamp":{"id":"paint.foldRamp","name":"foldRamp","qualifiedName":"foldRamp","category":"paint","module":"std/paint/fields.ts","summary":"Folds any number into 0–1 as a seamless zigzag: up from 0 to 1, back down, every 2 units.","description":"Scale the value first to set the cycle length. Safe anywhere, including inside a `guarded` branch.","examples":["const rgb = stops(p('colorSpace'))(foldRamp(phase), params).member('rgb')"],"tips":[],"see":["paint.wrapRamp"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.foldRamp"},"usedBy":["Obsidian"],"documented":true,"source":{"file":"std/paint/fields.ts","line":745},"kind":"function","signature":"foldRamp(t: Expr): Expr","params":[{"name":"t","type":"Expr","optional":false}]},"paint.warpedPoint":{"id":"paint.warpedPoint","name":"warpedPoint","qualifiedName":"warpedPoint","category":"paint","module":"std/paint/fields.ts","summary":"A 3D position pushed around by moving noise, for reading several fields at one warped spot.","description":"`at` is the 3D point to warp, `time` drives the motion, `amount` how far it is pushed. `levels` 2 (default) is the full warp, 1 is a cheaper single pass for low-power devices. Use `warped` instead when one field is enough.","examples":["const q = warpedPoint({at: math.vec3(x, y, seed), time: math.mul(t, 0.3), amount: 0.5, levels: isMobileGpuViewport() ? 1 : 2})"],"tips":[],"see":["paint.warped","paint.warpStep"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.warpedPoint"},"usedBy":["MeshGradient"],"documented":true,"source":{"file":"std/paint/fields.ts","line":764},"kind":"function","signature":"warpedPoint(opts: {at: Expr; time: Expr | number; amount: Expr | number; levels?: 1 | 2}): Expr","params":[{"name":"opts","type":"{at: Expr; time: Expr | number; amount: Expr | number; levels?: 1 | 2}","optional":false}]},"paint.warpStep":{"id":"paint.warpStep","name":"warpStep","qualifiedName":"warpStep","category":"paint","module":"std/paint/fields.ts","summary":"One step of a chained warp: pushes a 2D position by two noise reads and hands them back.","description":"Returns a vec4: `xy` is the warped position, `zw` the two noise values, useful for shading later. Chain steps by feeding one step's `xy` into the next. `scale` sets the noise frequency, `z` is a time or seed slice, `offsets` two fixed 2D offsets that keep the reads apart, and the push distance is `reach` times `strength`.","examples":["const level1 = warpStep({at: uv, scale: 1.2, z: math.mul(t, 0.5), offsets: [[5.2, 1.3], [1.7, 9.2]], reach: 0.4, strength: uniformOf(p('warp'), params)})\nconst level2 = warpStep({at: level1.member('xy'), scale: 2.4, z: t, offsets: [[8.3, 2.8], [2.1, 6.4]], reach: 0.25, strength: uniformOf(p('warp'), params)})"],"tips":[],"see":["paint.warpedPoint","paint.quadBlend"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.warpStep"},"usedBy":["FlowingGradient"],"documented":true,"source":{"file":"std/paint/fields.ts","line":791},"kind":"function","signature":"warpStep(opts: {\n    at: Expr\n    scale: number\n    z: Expr | number\n    offsets: [[number, number], [number, number]]\n    reach: number\n    strength: Expr | number\n}): Expr","params":[{"name":"opts","type":"{\n    at: Expr\n    scale: number\n    z: Expr | number\n    offsets: [[number, number], [number, number]]\n    reach: number\n    strength: Expr | number\n}","optional":false}]},"paint.standardPalette":{"id":"paint.standardPalette","name":"standardPalette","qualifiedName":"standardPalette","category":"paint","module":"std/paint/fields.ts","summary":"The color for a 0–1 value from the standard palette props: `colorA`, `colorB`, `stops`, `colorSpace`.","description":"Declare those four props (`stops: colorStopsPropConfig()`, `colorSpace` marked `compileTime`). It has the shape of a `Palette`, so it drops straight into `rampOver`, or call it yourself inside a `paint:` function.","examples":["paint: rampOver(scaledVolume(noiseField('mx3'), {scale: p('scale')}), standardPalette)"],"tips":[],"see":["paint.stops","paint.pair"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.standardPalette"},"usedBy":["MeshGradient"],"documented":true,"source":{"file":"std/paint/fields.ts","line":823},"kind":"function","signature":"standardPalette(t: Expr, params: GpuFragmentParams): Expr","params":[{"name":"t","type":"Expr","optional":false},{"name":"params","type":"GpuFragmentParams","optional":false}]},"paint.quadBlend":{"id":"paint.quadBlend","name":"quadBlend","qualifiedName":"quadBlend","category":"paint","module":"std/paint/fields.ts","summary":"A four-color blend on a square: `a` to `b` across the top, `c` to `d` across the bottom, then top to bottom.","description":"Returns a function you call with two 0–1 values (across, then down) and the params. Mixes in the color space named by the `space` prop (mark it `compileTime`). For any space other than linear, declare `extraFields` named `convA`, `convB`, `convC`, `convD`, each `{schema: schema.vec3f, initial: [0, 0, 0]}`.","examples":["const base = quadBlend({a: p('colorA'), b: p('colorB'), c: p('colorC'), d: p('colorD'), space: p('colorSpace')})(t1, t2, params)"],"tips":[],"see":["paint.colorLadder3","paint.pair"],"import":{"statement":"import {paint} from 'shaders/std'","usage":"paint.quadBlend"},"usedBy":["FlowingGradient"],"documented":true,"source":{"file":"std/paint/fields.ts","line":842},"kind":"function","signature":"quadBlend(slots: {a: PropRef; b: PropRef; c: PropRef; d: PropRef; space: PropRef}): (t1: Expr, t2: Expr, params: GpuFragmentParams) => Expr","params":[{"name":"slots","type":"{a: PropRef; b: PropRef; c: PropRef; d: PropRef; space: PropRef}","optional":false}]},"gradients.solidColor":{"id":"gradients.solidColor","name":"solidColor","qualifiedName":"solidColor","category":"gradients","module":"std/paint/gradients.ts","summary":"A flat fill in one color prop.","description":"The color's alpha passes through, so a half-transparent color gives a half-transparent layer.","examples":["paint: solidColor(p('color'))"],"tips":[],"see":[],"import":{"statement":"import {gradients} from 'shaders/std'","usage":"gradients.solidColor"},"usedBy":["SolidColor"],"documented":true,"source":{"file":"std/paint/gradients.ts","line":64},"kind":"function","signature":"solidColor(color: PropRef): Paint","params":[{"name":"color","type":"PropRef","optional":false}]},"gradients.colorWheel":{"id":"gradients.colorWheel","name":"colorWheel","qualifiedName":"colorWheel","category":"gradients","module":"std/paint/gradients.ts","summary":"A striped gradient that slides across the canvas, cycling through its colors over time.","description":"`mode` is a `compileTime` prop: `'rainbow'` generates a full spectrum, `'custom'` loops `palette.a` to `b` to `c` and back, mixed in the color space named by the `space` prop (also `compileTime`, ignored in rainbow mode). `direction` in degrees turns the stripes about the canvas center, `scale` sets how many color cycles cross the canvas (1 is a single wide sweep). It moves on the layer's clock, so declare `animatedTime: {speed: 'speed'}`.","examples":["paint: colorWheel({mode: p('mode'), direction: p('angle'), scale: p('scale'), palette: {a: p('colorA'), b: p('colorB'), c: p('colorC')}, space: p('colorSpace')})"],"tips":[],"see":["gradients.pointCloudGradient"],"import":{"statement":"import {gradients} from 'shaders/std'","usage":"gradients.colorWheel"},"usedBy":["ColorWheel"],"documented":true,"source":{"file":"std/paint/gradients.ts","line":84},"kind":"function","signature":"colorWheel(slots: {\n    mode: PropRef\n    direction: ArgSpec\n    scale: ArgSpec\n    palette: {a: PropRef; b: PropRef; c: PropRef}\n    space: PropRef\n}): Paint","params":[{"name":"slots","type":"{\n    mode: PropRef\n    direction: ArgSpec\n    scale: ArgSpec\n    palette: {a: PropRef; b: PropRef; c: PropRef}\n    space: PropRef\n}","optional":false}]},"gradients.pointCloudGradient":{"id":"gradients.pointCloudGradient","name":"pointCloudGradient","qualifiedName":"pointCloudGradient","category":"gradients","module":"std/paint/gradients.ts","summary":"Five colored points placed anywhere on the canvas, each pixel taking the colors of the points nearest it.","description":"Pass exactly five `{color, position}` prop pairs (positions made with `transformPosition`). `smoothness` (0–5) widens each point's reach as it rises. The mix happens in the color space named by the `space` prop (mark it `compileTime`).","examples":["paint: pointCloudGradient({points: [{color: p('colorA'), position: p('positionA')}, {color: p('colorB'), position: p('positionB')}, {color: p('colorC'), position: p('positionC')}, {color: p('colorD'), position: p('positionD')}, {color: p('colorE'), position: p('positionE')}], smoothness: p('smoothness'), space: p('colorSpace')})"],"tips":["For a softer, animated version with any number of points, see `scatterField` in the fields words."],"see":["gradients.colorWheel"],"import":{"statement":"import {gradients} from 'shaders/std'","usage":"gradients.pointCloudGradient"},"usedBy":["MultiPointGradient"],"documented":true,"source":{"file":"std/paint/gradients.ts","line":131},"kind":"function","signature":"pointCloudGradient(slots: {\n    points: {color: PropRef; position: PropRef}[]\n    smoothness: ArgSpec\n    space: PropRef\n}): Paint","params":[{"name":"slots","type":"{\n    points: {color: PropRef; position: PropRef}[]\n    smoothness: ArgSpec\n    space: PropRef\n}","optional":false}]},"gradients.beamPreconvertedFields":{"id":"gradients.beamPreconvertedFields","name":"beamPreconvertedFields","qualifiedName":"beamPreconvertedFields","category":"gradients","module":"std/paint/gradients.ts","summary":"The `extraFields` a `beam` definition must declare.","description":"Spread it as `extraFields: beamPreconvertedFields` next to the `beam` paint.","examples":["extraFields: beamPreconvertedFields,"],"tips":[],"see":["gradients.beam"],"import":{"statement":"import {gradients} from 'shaders/std'","usage":"gradients.beamPreconvertedFields"},"usedBy":["Beam"],"documented":true,"source":{"file":"std/paint/gradients.ts","line":169},"kind":"object","signature":"beamPreconvertedFields: {  }","members":[],"params":[]},"gradients.beam":{"id":"gradients.beam","name":"beam","qualifiedName":"beam","category":"gradients","module":"std/paint/gradients.ts","summary":"A glowing beam of light between two points.","description":"`from` and `to` are position props (made with `transformPosition`). `thickness` (0–2) and `softness` each take a `start` and `end` value so the beam can taper. The cross-section shades from `colors.inside` at the core to `colors.outside` at the edge, mixed in the color space named by the `space` prop (mark it `compileTime`). Declare `extraFields: beamPreconvertedFields` on the definition.","examples":["paint: beam({from: p('startPosition'), to: p('endPosition'), thickness: {start: p('startThickness'), end: p('endThickness')}, softness: {start: p('startSoftness'), end: p('endSoftness')}, colors: {inside: p('insideColor'), outside: p('outsideColor')}, space: p('colorSpace')})"],"tips":[],"see":["gradients.beamPreconvertedFields"],"import":{"statement":"import {gradients} from 'shaders/std'","usage":"gradients.beam"},"usedBy":["Beam"],"documented":true,"source":{"file":"std/paint/gradients.ts","line":194},"kind":"function","signature":"beam(slots: {\n    from: PropRef\n    to: PropRef\n    thickness: {start: ArgSpec; end: ArgSpec}\n    softness: {start: ArgSpec; end: ArgSpec}\n    colors: {inside: PropRef; outside: PropRef}\n    space: PropRef\n}): Paint","params":[{"name":"slots","type":"{\n    from: PropRef\n    to: PropRef\n    thickness: {start: ArgSpec; end: ArgSpec}\n    softness: {start: ArgSpec; end: ArgSpec}\n    colors: {inside: PropRef; outside: PropRef}\n    space: PropRef\n}","optional":false}]},"patterns.cellFrame":{"id":"patterns.cellFrame","name":"cellFrame","qualifiedName":"cellFrame","category":"patterns","module":"std/paint/patterns.ts","summary":"The shared coordinate system of a tiled pattern: how many cells fit down the canvas height and how far the whole lattice is rotated.","description":"`cells` counts cells along the canvas height, so cells stay square as the canvas resizes. `rotation` is in degrees about the canvas centre. Pass the result as the `frame` of `gridLines`, `hexLines`, `triangleLines`, `truchetArcs`, `weaveThreads` or `isoCubeFaces`.","examples":["const field = gridLines({frame: cellFrame({cells: p('cells'), rotation: p('rotation'), convention: 'flippedY'}), thickness: p('thickness'), softness: p('softness'), variation: p('variation')})"],"tips":["One frame can feed several line words. It is evaluated once however many read it."],"see":["patterns.gridLines","patterns.hexLines","patterns.triangleLines","patterns.truchetArcs","patterns.weaveThreads","patterns.isoCubeFaces"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.cellFrame"},"usedBy":["Grid","HexGrid","IsometricCubes","TriangularGrid","Truchet","Weave"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":97},"kind":"function","signature":"cellFrame(slots: {cells: PropRef; rotation: PropRef; convention: CellFrameConvention}): PatternPaint","params":[{"name":"slots","type":"{cells: PropRef; rotation: PropRef; convention: CellFrameConvention}","optional":false}]},"patterns.vary":{"id":"patterns.vary","name":"vary","qualifiedName":"vary","category":"patterns","module":"std/paint/patterns.ts","summary":"A color lightened or darkened per cell by a shade factor, alpha untouched.","description":"`factor` is the `shade` output of a lattice word: 1 keeps the color, above 1 brightens, below 1 darkens. Use it on the cell fill so every tile reads slightly different.","examples":["paint: strokeOver({fill: vary(p('cellColor'), field.shade), stroke: p('color'), mask: field.lines, space: p('colorSpace')})"],"tips":[],"see":["patterns.gridLines","patterns.hexLines","patterns.brickCourses","patterns.strokeOver"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.vary"},"usedBy":["BrickPattern","Grid","HexGrid","TriangularGrid"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":119},"kind":"function","signature":"vary(color: PaintSlot, factor: PaintSlot): PatternPaint","params":[{"name":"color","type":"PaintSlot","optional":false},{"name":"factor","type":"PaintSlot","optional":false}]},"patterns.strokeOver":{"id":"patterns.strokeOver","name":"strokeOver","qualifiedName":"strokeOver","category":"patterns","module":"std/paint/patterns.ts","summary":"Two colors mixed through a mask: `fill` where the mask is 0, `stroke` where it is 1.","description":"The everyday way to color a pattern. `space` is a color-space prop (`'linear'`, `'oklch'`, `'oklab'`, `'hsl'`, `'hsv'`, `'lch'`); leave it out for a linear mix. Alpha interpolates too, so a transparent fill gives a lines-only pattern.","examples":["paint: strokeOver({fill: p('colorA'), stroke: p('colorB'), mask: checkerCells({cells: p('cells'), softness: p('softness')}), space: p('colorSpace')})"],"tips":["The `space` prop must be `compileTime: true`. Changing it recompiles the shader."],"see":["patterns.withAlpha","patterns.mixOf","patterns.vary"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.strokeOver"},"usedBy":["BrickPattern","Checkerboard","Chevron","FallingLines","Grid","HexGrid","IsometricCubes","Ripples","Stripes","TriangularGrid","Truchet"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":140},"kind":"function","signature":"strokeOver(layers: {fill: PaintSlot; stroke: PaintSlot; mask: PaintSlot; space?: PropRef}): PatternPaint","params":[{"name":"layers","type":"{fill: PaintSlot; stroke: PaintSlot; mask: PaintSlot; space?: PropRef}","optional":false}]},"patterns.sampleMapsAtCellCentres":{"id":"patterns.sampleMapsAtCellCentres","name":"sampleMapsAtCellCentres","qualifiedName":"sampleMapsAtCellCentres","category":"patterns","module":"std/paint/patterns.ts","summary":"Makes map-driven props change per whole cell instead of per pixel, so a mapped dot size or line thickness grows cell by cell rather than being cut off mid-cell.","description":"Set it on the definition's `mapSampleUVs` field, not in `paint:`. Give it the same `cells` count and `rotation` (degrees) the pattern uses so the snapping matches the lattice. `props` lists the prop names to snap.","examples":["mapSampleUVs: sampleMapsAtCellCentres({cells: p('cells'), rotation: p('rotation'), props: ['thickness', 'softness']})"],"tips":["Only matters when a prop is driven by a map (an image or another layer). Plain values are unaffected."],"see":["patterns.cellFrame","patterns.dotLattice","patterns.gridLines"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.sampleMapsAtCellCentres"},"usedBy":["DotGrid","Grid"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":163},"kind":"function","signature":"sampleMapsAtCellCentres(slots: {\n    cells: PropRef\n    rotation?: PropRef\n    props: string[]\n}): GpuMapSampleUVs","params":[{"name":"slots","type":"{\n    cells: PropRef\n    rotation?: PropRef\n    props: string[]\n}","optional":false}]},"patterns.scatterPoint":{"id":"patterns.scatterPoint","name":"scatterPoint","qualifiedName":"scatterPoint","category":"patterns","module":"std/paint/patterns.ts","summary":"One random point in each cell of a square grid: the classic scatter for glitter, stars, dust and sparkles.","description":"`coord` is the pixel's position in any unit (CSS pixels via `uv × logicalViewportSize` keeps the spacing steady across screens); `cellSize` is the grid spacing in that unit. `jitter` is how far the point wanders from the cell's centre, 0 (dead centre) to 1 (anywhere in the cell). `seed` re-rolls every point. Returns the cell's `point` and this pixel's `offset` from it (both in `coord`'s unit), plus `random`, three independent 0–1 values for the cell (`.x` and `.y` already place the point; use `.z` and the others to vary brightness, timing or size).","examples":["const {offset, random} = scatterPoint(px, {cellSize: 30, jitter: 0.6, seed: params.uniforms.seed})"],"tips":["A pixel only sees its own cell's point, so keep anything drawn around it smaller than\nhalf a cell or it gets clipped at the cell edge. Use `scatterPoints` for larger shapes."],"see":["patterns.scatterPoints","patterns.cellFrame","materials.pointStars"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.scatterPoint"},"usedBy":[],"documented":true,"source":{"file":"std/paint/patterns.ts","line":204},"kind":"function","signature":"scatterPoint(coord: Expr, opts: ScatterOptions): ScatteredPoint","params":[{"name":"coord","type":"Expr","optional":false},{"name":"opts","type":"ScatterOptions","optional":false}]},"patterns.scatterPoints":{"id":"patterns.scatterPoints","name":"scatterPoints","qualifiedName":"scatterPoints","category":"patterns","module":"std/paint/patterns.ts","summary":"The scattered points of the four grid cells nearest the pixel, so anything drawn up to half a cell from its point shows whole, never clipped at a cell edge.","description":"Same options and same points as `scatterPoint` (one random point per cell); returns four of them instead of one. Draw something around each and add the results. Costs four times `scatterPoint`.","examples":["const glints = scatterPoints(px, {cellSize: 60, jitter: 0.6, seed: params.uniforms.seed})\n    .map(({offset}) => starGlint(offset, {core: 0.9, rayLength: 0.1, rayWidth: 1.6, rays: 0.25, reach: 30}))"],"tips":["Pair with `starGlint`'s `reach` set to half the cell size, so a glint has faded out\nbefore it could reach a cell this pixel doesn't look at."],"see":["patterns.scatterPoint","light.starGlint"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.scatterPoints"},"usedBy":["Sparkle"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":226},"kind":"function","signature":"scatterPoints(coord: Expr, opts: ScatterOptions): ScatteredPoint[]","params":[{"name":"coord","type":"Expr","optional":false},{"name":"opts","type":"ScatterOptions","optional":false}]},"patterns.withAlpha":{"id":"patterns.withAlpha","name":"withAlpha","qualifiedName":"withAlpha","category":"patterns","module":"std/paint/patterns.ts","summary":"A color with its alpha replaced by a separately built mask.","description":"The usual way to hang a pattern under one color: the mask becomes the coverage and the color's own alpha is dropped.","examples":["paint: withAlpha(p('color'), dotLattice({density: p('density'), dotSize: p('dotSize'), offset: p('offset'), speedVariance: p('speedVariance'), twinkle: p('twinkle')}))"],"tips":["To keep the color's own alpha as well, multiply it in: `withAlpha(color, times(alphaOf(color), mask))`."],"see":["patterns.alphaOf","patterns.times","patterns.opaque"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.withAlpha"},"usedBy":["Chevron","DotGrid","FallingLines"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":272},"kind":"function","signature":"withAlpha(color: PaintSlot, alpha: PaintSlot): PatternPaint","params":[{"name":"color","type":"PaintSlot","optional":false},{"name":"alpha","type":"PaintSlot","optional":false}]},"patterns.alphaOf":{"id":"patterns.alphaOf","name":"alphaOf","qualifiedName":"alphaOf","category":"patterns","module":"std/paint/patterns.ts","summary":"The alpha channel of a color, as a number per pixel.","description":"","examples":["paint: withAlpha(streakColor, times(alphaOf(streakColor), streaks.mask))"],"tips":[],"see":["patterns.rgbOf","patterns.withAlpha"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.alphaOf"},"usedBy":["Chevron","FallingLines"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":288},"kind":"function","signature":"alphaOf(color: PaintSlot): PatternPaint","params":[{"name":"color","type":"PaintSlot","optional":false}]},"patterns.rgbOf":{"id":"patterns.rgbOf","name":"rgbOf","qualifiedName":"rgbOf","category":"patterns","module":"std/paint/patterns.ts","summary":"The RGB channels of a color, without its alpha.","description":"Finish it with `opaque` or `withAlpha` to make a color again.","examples":["paint: opaque(mixOf(times(rgbOf(cubeColor), faces.tone), rgbOf(p('lineColor')), faces.wire))"],"tips":[],"see":["patterns.alphaOf","patterns.opaque"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.rgbOf"},"usedBy":["IsometricCubes"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":303},"kind":"function","signature":"rgbOf(color: PaintSlot): PatternPaint","params":[{"name":"color","type":"PaintSlot","optional":false}]},"patterns.times":{"id":"patterns.times","name":"times","qualifiedName":"times","category":"patterns","module":"std/paint/patterns.ts","summary":"The product of two values: a mask scaled by a prop, an RGB dimmed by a shade, two masks combined.","description":"","examples":["mask: times(faces.random, p('colorVariation'))"],"tips":[],"see":["patterns.mixOf","patterns.vary"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.times"},"usedBy":["FallingLines","IsometricCubes"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":317},"kind":"function","signature":"times(a: PaintSlot, b: PaintSlot): PatternPaint","params":[{"name":"a","type":"PaintSlot","optional":false},{"name":"b","type":"PaintSlot","optional":false}]},"patterns.mixOf":{"id":"patterns.mixOf","name":"mixOf","qualifiedName":"mixOf","category":"patterns","module":"std/paint/patterns.ts","summary":"A straight blend from one value to another by an amount from 0 to 1.","description":"Works on colors, RGB and single numbers alike, channel by channel with no color-space handling. For a color mix in a chosen color space use `strokeOver`.","examples":["mixOf(alphaOf(p('colorA')), alphaOf(p('colorB')), bands)"],"tips":[],"see":["patterns.strokeOver","patterns.times"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.mixOf"},"usedBy":["Chevron","IsometricCubes"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":333},"kind":"function","signature":"mixOf(from: PaintSlot, to: PaintSlot, amount: PaintSlot): PatternPaint","params":[{"name":"from","type":"PaintSlot","optional":false},{"name":"to","type":"PaintSlot","optional":false},{"name":"amount","type":"PaintSlot","optional":false}]},"patterns.opaque":{"id":"patterns.opaque","name":"opaque","qualifiedName":"opaque","category":"patterns","module":"std/paint/patterns.ts","summary":"An RGB value finished as a fully opaque color.","description":"","examples":["paint: opaque(mixOf(times(rgbOf(cubeColor), faces.tone), rgbOf(p('lineColor')), faces.wire))"],"tips":[],"see":["patterns.rgbOf","patterns.withAlpha"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.opaque"},"usedBy":["IsometricCubes"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":346},"kind":"function","signature":"opaque(rgb: PaintSlot): PatternPaint","params":[{"name":"rgb","type":"PaintSlot","optional":false}]},"patterns.checkerCells":{"id":"patterns.checkerCells","name":"checkerCells","qualifiedName":"checkerCells","category":"patterns","module":"std/paint/patterns.ts","summary":"A checkerboard mask: 1 on one set of squares, 0 on the other, with clean edges at any size.","description":"`cells` counts squares along the canvas height (squares stay square as the canvas resizes). `softness` 0–1 blurs the edges; 0 is crisp. Color it with `strokeOver`.","examples":["paint: strokeOver({fill: p('colorA'), stroke: p('colorB'), mask: checkerCells({cells: p('cells'), softness: p('softness')}), space: p('colorSpace')})"],"tips":["Stays sharp inside a distortion, which makes it a good test pattern for warps."],"see":["patterns.stripeBands","patterns.gridLines","patterns.strokeOver"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.checkerCells"},"usedBy":["Checkerboard"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":365},"kind":"function","signature":"checkerCells(slots: {cells: PropRef; softness: PropRef}): PatternPaint","params":[{"name":"slots","type":"{cells: PropRef; softness: PropRef}","optional":false}]},"patterns.stripeBands":{"id":"patterns.stripeBands","name":"stripeBands","qualifiedName":"stripeBands","category":"patterns","module":"std/paint/patterns.ts","summary":"A mask of straight parallel stripes that scroll with the layer's clock.","description":"`angle` is in degrees. `density` is the number of stripe pairs across the canvas height. `balance` 0–1 is how much of each pair is the 0 side (the fill in `strokeOver`). `softness` 0–1 blurs the edges. `offset` shifts the phase; 1 is one whole pair. Declare `animatedTime: {speed: 'speed'}` on the definition; speed 0 holds the stripes still.","examples":["paint: strokeOver({fill: p('colorA'), stroke: p('colorB'), mask: stripeBands({angle: p('angle'), density: p('density'), balance: p('balance'), softness: p('softness'), offset: p('offset')}), space: p('colorSpace')})"],"tips":[],"see":["patterns.zigzagBands","patterns.checkerCells","patterns.ringWaves"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.stripeBands"},"usedBy":["Stripes"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":390},"kind":"function","signature":"stripeBands(slots: {\n    angle: PropRef\n    density: PropRef\n    balance: PropRef\n    softness: PropRef\n    offset: PropRef\n}): PatternPaint","params":[{"name":"slots","type":"{\n    angle: PropRef\n    density: PropRef\n    balance: PropRef\n    softness: PropRef\n    offset: PropRef\n}","optional":false}]},"patterns.zigzagBands":{"id":"patterns.zigzagBands","name":"zigzagBands","qualifiedName":"zigzagBands","category":"patterns","module":"std/paint/patterns.ts","summary":"A mask of chevron (zigzag) stripes that scroll with the layer's clock.","description":"`count` is the number of stripe pairs across the canvas. `angle` is in degrees. `balance` 0–1 is how much of each pair is the 0 side. `softness` 0–1 blurs the edges. `offset` shifts the phase; 1 is one whole pair. Declare `animatedTime: {speed: 'speed'}` on the definition.","examples":["const bands = zigzagBands({count: p('count'), angle: p('angle'), balance: p('balance'), softness: p('softness'), offset: p('offset')})"],"tips":["Reuse one `bands` value for both the color mix and an alpha mix. It is evaluated once."],"see":["patterns.stripeBands","patterns.checkerCells"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.zigzagBands"},"usedBy":["Chevron"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":429},"kind":"function","signature":"zigzagBands(slots: {\n    count: PropRef\n    angle: PropRef\n    balance: PropRef\n    softness: PropRef\n    offset: PropRef\n}): PatternPaint","params":[{"name":"slots","type":"{\n    count: PropRef\n    angle: PropRef\n    balance: PropRef\n    softness: PropRef\n    offset: PropRef\n}","optional":false}]},"patterns.ringWaves":{"id":"patterns.ringWaves","name":"ringWaves","qualifiedName":"ringWaves","category":"patterns","module":"std/paint/patterns.ts","summary":"A mask of concentric rings spreading out from a point, animated by the layer's clock.","description":"`center` is a position prop. `frequency` sets how tightly the rings pack; higher is more rings. `thickness` 0–1 is how much of each ring period is ring (0.5 is even bands, 1 is solid). `softness` 0–3 blurs the ring edges. `phase` is in radians. Declare `animatedTime: {speed: 'speed'}` on the definition; positive speed moves the rings outward.","examples":["paint: strokeOver({fill: p('colorB'), stroke: p('colorA'), mask: ringWaves({center: p('center'), frequency: p('frequency'), thickness: p('thickness'), softness: p('softness'), phase: p('phase')})})"],"tips":["Keep `softness` above 0. At exactly 0 the edge is an undefined hard step rather than a crisp one."],"see":["patterns.stripeBands","patterns.dotLattice"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.ringWaves"},"usedBy":["Ripples"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":467},"kind":"function","signature":"ringWaves(slots: {\n    center: PropRef\n    frequency: PropRef\n    thickness: PropRef\n    softness: PropRef\n    phase: PropRef\n}): PatternPaint","params":[{"name":"slots","type":"{\n    center: PropRef\n    frequency: PropRef\n    thickness: PropRef\n    softness: PropRef\n    phase: PropRef\n}","optional":false}]},"patterns.fallingStreaks":{"id":"patterns.fallingStreaks","name":"fallingStreaks","qualifiedName":"fallingStreaks","category":"patterns","module":"std/paint/patterns.ts","summary":"Streaks that fall across the canvas in columns, each column at its own random speed.","description":"Returns two values: `mask` is the streak coverage, and `fade` runs from 0 at the tail to 1 at the leading edge, for a color ramp along each streak. `angle` is in degrees (90 falls down, 0 moves right). `density` is the number of columns across the canvas. `speedVariance` 0–1 spreads the per-column speeds. `trailLength` 0–1 is the streak length as a fraction of the spacing. `strokeWidth` 0–1 is the width as a fraction of a column. `rounding` 0–1 rounds the leading cap. `balance` 0–1 moves the midpoint of `fade`. Declare `animatedTime: {speed: 'speed'}` on the definition.","examples":["const streaks = fallingStreaks({angle: p('angle'), density: p('density'), speedVariance: p('speedVariance'), trailLength: p('trailLength'), strokeWidth: p('strokeWidth'), rounding: p('rounding'), balance: p('balance')})\nconst streakColor = strokeOver({fill: p('trailColor'), stroke: p('leadColor'), mask: streaks.fade, space: p('colorSpace')})\n// paint: withAlpha(streakColor, times(alphaOf(streakColor), streaks.mask))"],"tips":[],"see":["patterns.stripeBands","patterns.dotLattice","patterns.withAlpha"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.fallingStreaks"},"usedBy":["FallingLines"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":511},"kind":"function","signature":"fallingStreaks(slots: {\n    angle: PropRef\n    density: PropRef\n    speedVariance: PropRef\n    trailLength: PropRef\n    strokeWidth: PropRef\n    rounding: PropRef\n    balance: PropRef\n}): {mask: PatternPaint; fade: PatternPaint}","params":[{"name":"slots","type":"{\n    angle: PropRef\n    density: PropRef\n    speedVariance: PropRef\n    trailLength: PropRef\n    strokeWidth: PropRef\n    rounding: PropRef\n    balance: PropRef\n}","optional":false}]},"patterns.dotLattice":{"id":"patterns.dotLattice","name":"dotLattice","qualifiedName":"dotLattice","category":"patterns","module":"std/paint/patterns.ts","summary":"A mask of round dots on a square grid, with optional row stagger, drift and twinkle.","description":"`density` counts dots along the canvas height. `dotSize` 0–1 is the dot diameter as a fraction of a cell; 1 touches the neighbours. `offset` 0–1 shifts every other row (0.5 is the polka-dot stagger). `speedVariance` 0–1 gives rows different drift speeds. `twinkle` 0–1 pulses each dot on its own phase. Declare `animatedTime: {speed: 'speed'}` on the definition; the drift follows that clock while the twinkle keeps running at speed 0.","examples":["paint: withAlpha(p('color'), dotLattice({density: p('density'), dotSize: p('dotSize'), offset: p('offset'), speedVariance: p('speedVariance'), twinkle: p('twinkle')}))"],"tips":["Add `sampleMapsAtCellCentres` when `dotSize` or `twinkle` is map-driven, so dots grow whole instead of being clipped."],"see":["patterns.sampleMapsAtCellCentres","patterns.gridLines","patterns.ringWaves"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.dotLattice"},"usedBy":["DotGrid"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":556},"kind":"function","signature":"dotLattice(slots: {\n    density: PropRef\n    dotSize: PropRef\n    offset: PropRef\n    speedVariance: PropRef\n    twinkle: PropRef\n}): PatternPaint","params":[{"name":"slots","type":"{\n    density: PropRef\n    dotSize: PropRef\n    offset: PropRef\n    speedVariance: PropRef\n    twinkle: PropRef\n}","optional":false}]},"patterns.gridLines":{"id":"patterns.gridLines","name":"gridLines","qualifiedName":"gridLines","category":"patterns","module":"std/paint/patterns.ts","summary":"Square grid lines drawn in a `cellFrame`, plus a per-cell shade for the fill.","description":"Returns `lines` (1 on a line, 0 inside a cell) and `shade` (a brightness factor around 1 for `vary`). `thickness` 1 draws lines about 2% of a cell wide; 0 draws none. `softness` 0–1 blurs the lines. `variation` 0–1 sets how far `shade` strays from 1. Draw it with the `'flippedY'` convention.","examples":["const field = gridLines({frame: cellFrame({cells: p('cells'), rotation: p('rotation'), convention: 'flippedY'}), thickness: p('thickness'), softness: p('softness'), variation: p('variation')})\n// paint: strokeOver({fill: vary(p('cellColor'), field.shade), stroke: p('color'), mask: field.lines, space: p('colorSpace')})"],"tips":["Rotate the frame 45 degrees for a diamond crosshatch."],"see":["patterns.cellFrame","patterns.hexLines","patterns.triangleLines","patterns.vary"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.gridLines"},"usedBy":["Grid"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":598},"kind":"function","signature":"gridLines(slots: {\n    frame: PatternPaint\n    thickness: PropRef\n    softness: PropRef\n    variation: PropRef\n}): {lines: PatternPaint; shade: PatternPaint}","params":[{"name":"slots","type":"{\n    frame: PatternPaint\n    thickness: PropRef\n    softness: PropRef\n    variation: PropRef\n}","optional":false}]},"patterns.hexLines":{"id":"patterns.hexLines","name":"hexLines","qualifiedName":"hexLines","category":"patterns","module":"std/paint/patterns.ts","summary":"Honeycomb lines (pointy-top hexagons) drawn in a `cellFrame`, plus a per-cell shade.","description":"Returns `lines` (1 on a line) and `shade` (a brightness factor around 1 for `vary`). `thickness` 1 draws lines about 2% of a cell wide; 0 draws none. `softness` 0–1 blurs the lines. `variation` 0–1 sets how far `shade` strays from 1. Draw it with the `'clockwise'` convention.","examples":["const field = hexLines({frame: cellFrame({cells: p('cells'), rotation: p('rotation'), convention: 'clockwise'}), thickness: p('thickness'), softness: p('softness'), variation: p('variation')})"],"tips":[],"see":["patterns.cellFrame","patterns.gridLines","patterns.isoCubeFaces","patterns.vary"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.hexLines"},"usedBy":["HexGrid"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":626},"kind":"function","signature":"hexLines(slots: {\n    frame: PatternPaint\n    thickness: PropRef\n    softness: PropRef\n    variation: PropRef\n}): {lines: PatternPaint; shade: PatternPaint}","params":[{"name":"slots","type":"{\n    frame: PatternPaint\n    thickness: PropRef\n    softness: PropRef\n    variation: PropRef\n}","optional":false}]},"patterns.triangleLines":{"id":"patterns.triangleLines","name":"triangleLines","qualifiedName":"triangleLines","category":"patterns","module":"std/paint/patterns.ts","summary":"A lattice of equilateral triangles drawn in a `cellFrame`, with rows that can drift.","description":"Returns `lines` (1 on a line) and `shade` (a brightness factor around 1 per triangle for `vary`). `thickness` 1 draws lines about 4% of a cell wide; 0 draws none. `softness` 0–1 blurs the lines. `variation` 0–1 sets how far `shade` strays from 1. `speedVariance` 0–1 gives rows different drift speeds. Declare `animatedTime: {speed: 'speed'}` on the definition; speed 0 holds still. Draw it with the `'flippedY'` convention.","examples":["const field = triangleLines({frame: cellFrame({cells: p('cells'), rotation: p('rotation'), convention: 'flippedY'}), thickness: p('thickness'), softness: p('softness'), variation: p('variation'), speedVariance: p('speedVariance')})"],"tips":[],"see":["patterns.cellFrame","patterns.gridLines","patterns.hexLines","patterns.vary"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.triangleLines"},"usedBy":["TriangularGrid"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":655},"kind":"function","signature":"triangleLines(slots: {\n    frame: PatternPaint\n    thickness: PropRef\n    softness: PropRef\n    variation: PropRef\n    speedVariance: PropRef\n}): {lines: PatternPaint; shade: PatternPaint}","params":[{"name":"slots","type":"{\n    frame: PatternPaint\n    thickness: PropRef\n    softness: PropRef\n    variation: PropRef\n    speedVariance: PropRef\n}","optional":false}]},"patterns.brickCourses":{"id":"patterns.brickCourses","name":"brickCourses","qualifiedName":"brickCourses","category":"patterns","module":"std/paint/patterns.ts","summary":"A brick wall: staggered rows with mortar gaps, optional rotation and per-row drift.","description":"Returns `bricks` (1 on a brick, 0 in the mortar) and `shade` (a brightness factor around 1 per brick for `vary`). `cellsX` is bricks per row across the canvas width and `cellsY` the number of rows down its height. `mortar` 0–1 widens the gaps (1 is 30% of a brick), equal in pixels both ways. `softness` 0–1 blurs the edges. `variation` 0–1 sets how far `shade` strays from 1. `rotation` is in degrees. `offset` slides the rows; 1 is one brick. `speedVariance` 0–1 and `seed` randomise the per-row drift. Declare `animatedTime: {speed: 'speed'}` on the definition. It brings its own frame, so there is no `frame` slot.","examples":["const field = brickCourses({cellsX: p('cellsX'), cellsY: p('cellsY'), mortar: p('mortar'), softness: p('softness'), variation: p('variation'), rotation: p('rotation'), offset: p('offset'), speedVariance: p('speedVariance'), seed: p('seed')})\n// paint: strokeOver({fill: p('colorMortar'), stroke: vary(p('colorBrick'), field.shade), mask: field.bricks, space: p('colorSpace')})"],"tips":[],"see":["patterns.gridLines","patterns.cellFrame","patterns.vary"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.brickCourses"},"usedBy":["BrickPattern"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":694},"kind":"function","signature":"brickCourses(slots: {\n    cellsX: PropRef\n    cellsY: PropRef\n    mortar: PropRef\n    softness: PropRef\n    variation: PropRef\n    rotation: PropRef\n    offset: PropRef\n    speedVariance: PropRef\n    seed: PropRef\n}): {bricks: PatternPaint; shade: PatternPaint}","params":[{"name":"slots","type":"{\n    cellsX: PropRef\n    cellsY: PropRef\n    mortar: PropRef\n    softness: PropRef\n    variation: PropRef\n    rotation: PropRef\n    offset: PropRef\n    speedVariance: PropRef\n    seed: PropRef\n}","optional":false}]},"patterns.truchetArcs":{"id":"patterns.truchetArcs","name":"truchetArcs","qualifiedName":"truchetArcs","category":"patterns","module":"std/paint/patterns.ts","summary":"A maze of quarter-circle arcs, two per tile, flipped at random so they join into flowing curves.","description":"Returns the arc mask (1 on an arc). `thickness` 2 draws arcs about 2% of a tile wide; 0 draws none. `softness` 0–1 blurs the edges. `seed` picks a different set of flips. Draw it in a `'clockwise'` `cellFrame`.","examples":["paint: strokeOver({fill: p('colorA'), stroke: p('colorB'), mask: truchetArcs({frame: cellFrame({cells: p('cells'), rotation: p('rotation'), convention: 'clockwise'}), thickness: p('thickness'), softness: p('softness'), seed: p('seed')}), space: p('colorSpace')})"],"tips":[],"see":["patterns.cellFrame","patterns.gridLines","patterns.weaveThreads"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.truchetArcs"},"usedBy":["Truchet"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":742},"kind":"function","signature":"truchetArcs(slots: {\n    frame: PatternPaint\n    thickness: PropRef\n    softness: PropRef\n    seed: PropRef\n}): PatternPaint","params":[{"name":"slots","type":"{\n    frame: PatternPaint\n    thickness: PropRef\n    softness: PropRef\n    seed: PropRef\n}","optional":false}]},"patterns.weaveThreads":{"id":"patterns.weaveThreads","name":"weaveThreads","qualifiedName":"weaveThreads","category":"patterns","module":"std/paint/patterns.ts","summary":"Two sets of threads woven over and under each other, returned as a finished color.","description":"`gap` 0–0.5 is the empty margin on each side of a thread, as a fraction of a cell; 0 packs them tight. `colors` is `[horizontal, vertical]`. The gaps are transparent, so this goes straight into `paint:`. Draw it in a `'plain'` `cellFrame`.","examples":["paint: weaveThreads({frame: cellFrame({cells: p('cells'), rotation: p('rotation'), convention: 'plain'}), gap: p('gap'), colors: [p('colorA'), p('colorB')]})"],"tips":[],"see":["patterns.cellFrame","patterns.gridLines","patterns.truchetArcs"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.weaveThreads"},"usedBy":["Weave"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":768},"kind":"function","signature":"weaveThreads(slots: {\n    frame: PatternPaint\n    gap: PropRef\n    /** `[horizontal, vertical]` thread colors. */\n    colors: [PropRef, PropRef]\n}): PatternPaint","params":[{"name":"slots","type":"{\n    frame: PatternPaint\n    gap: PropRef\n    /** `[horizontal, vertical]` thread colors. */\n    colors: [PropRef, PropRef]\n}","optional":false}]},"patterns.isoCubeFaces":{"id":"patterns.isoCubeFaces","name":"isoCubeFaces","qualifiedName":"isoCubeFaces","category":"patterns","module":"std/paint/patterns.ts","summary":"Tumbling blocks: three shaded rhombus faces per hexagon that read as a field of 3D cubes.","description":"Returns three values. `tone` is the face shading (1 on top, 0.74 on the right, 0.5 on the left) to multiply into the cube color. `random` is a 0–1 value per cube for varying cube colors. `wire` is the edge-line mask (1 on an edge). `thickness` 1 draws edges about 4% of a cell wide; 0 draws none. `softness` 0–1 blurs them. Draw it in a `'clockwise'` `cellFrame`.","examples":["const faces = isoCubeFaces({frame: cellFrame({cells: p('cells'), rotation: p('rotation'), convention: 'clockwise'}), thickness: p('thickness'), softness: p('softness')})\n// paint: opaque(mixOf(times(rgbOf(cubeColor), faces.tone), rgbOf(p('lineColor')), faces.wire))"],"tips":[],"see":["patterns.cellFrame","patterns.hexLines","patterns.times","patterns.opaque"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.isoCubeFaces"},"usedBy":["IsometricCubes"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":801},"kind":"function","signature":"isoCubeFaces(slots: {\n    frame: PatternPaint\n    thickness: PropRef\n    softness: PropRef\n}): {random: PatternPaint; tone: PatternPaint; wire: PatternPaint}","params":[{"name":"slots","type":"{\n    frame: PatternPaint\n    thickness: PropRef\n    softness: PropRef\n}","optional":false}]},"patterns.dotScreenMask":{"id":"patterns.dotScreenMask","name":"dotScreenMask","qualifiedName":"dotScreenMask","category":"patterns","module":"std/paint/patterns.ts","summary":"The dot coverage of one halftone screen: 1 inside a dot, 0 between, with the dot size set by an ink amount.","description":"A raw building block for a custom press inside a gather `build`. `uv` is the pixel's uv and `aspect` the canvas aspect (`ctx.uv`, `ctx.aspect`). `angle` is the screen angle in degrees. `intensity` 0–1 is the ink amount at this pixel; 0 leaves no dot. `frequency` is the number of dots down the canvas height.","examples":["const dots = dotScreenMask(ctx.uv, ctx.aspect, uniforms.angle, intensity, uniforms.frequency)"],"tips":[],"see":["patterns.inkTransmission","patterns.cmykPress","patterns.dotScreen"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.dotScreenMask"},"usedBy":[],"documented":true,"source":{"file":"std/paint/patterns.ts","line":835},"kind":"function","signature":"dotScreenMask(uv: Expr, aspect: Expr, angle: Expr, intensity: Expr, frequency: Expr): Expr","params":[{"name":"uv","type":"Expr","optional":false},{"name":"aspect","type":"Expr","optional":false},{"name":"angle","type":"Expr","optional":false},{"name":"intensity","type":"Expr","optional":false},{"name":"frequency","type":"Expr","optional":false}]},"patterns.inkTransmission":{"id":"patterns.inkTransmission","name":"inkTransmission","qualifiedName":"inkTransmission","category":"patterns","module":"std/paint/patterns.ts","summary":"How much light passes one ink plate: white where the dot mask is 0, the ink color where it is 1.","description":"Returns RGB. Multiply the plates together over a paper color to lay the inks down in order. The ink's alpha scales its strength.","examples":["printed = printed.mul(inkTransmission(uniforms.cyanColor, dotScreenMask(ctx.uv, ctx.aspect, uniforms.cyanAngle, cyan, uniforms.frequency)))"],"tips":[],"see":["patterns.dotScreenMask","patterns.cmykPress"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.inkTransmission"},"usedBy":[],"documented":true,"source":{"file":"std/paint/patterns.ts","line":853},"kind":"function","signature":"inkTransmission(inkColor: Expr, inkMask: Expr): Expr","params":[{"name":"inkColor","type":"Expr","optional":false},{"name":"inkMask","type":"Expr","optional":false}]},"patterns.dotScreen":{"id":"patterns.dotScreen","name":"dotScreen","qualifiedName":"dotScreen","category":"patterns","module":"std/paint/patterns.ts","summary":"A classic halftone filter: the layer inside it redrawn as a grid of dots that grow where it is bright.","description":"Goes in `effect:`. `angle` is the screen angle in degrees (45 is the print default). `frequency` is the number of dots down the canvas height. The dots keep the child's own color; between dots the output is transparent.","examples":["effect: dotScreen({angle: p('angle'), frequency: p('frequency')})"],"tips":[],"see":["patterns.cmykPress","patterns.chosenBy","patterns.quantise"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.dotScreen"},"usedBy":["Halftone"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":873},"kind":"function","signature":"dotScreen(slots: {angle: PropRef; frequency: PropRef}): GatherEffect","params":[{"name":"slots","type":"{angle: PropRef; frequency: PropRef}","optional":false}]},"patterns.inkPlate":{"id":"patterns.inkPlate","name":"inkPlate","qualifiedName":"inkPlate","category":"patterns","module":"std/paint/patterns.ts","summary":"Declares one ink plate for `cmykPress`.","description":"`channel` is `'cyan'`, `'magenta'`, `'yellow'` or `'black'`. `screenAngle` is an angle prop in degrees and `ink` a color prop. Plates print in the order you list them.","examples":["inkPlate({channel: 'cyan', screenAngle: p('cyanAngle'), ink: p('cyanColor')})"],"tips":[],"see":["patterns.cmykPress"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.inkPlate"},"usedBy":["Halftone"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":909},"kind":"function","signature":"inkPlate(spec: InkPlateSpec): InkPlateSpec","params":[{"name":"spec","type":"InkPlateSpec","optional":false}]},"patterns.cmykPress":{"id":"patterns.cmykPress","name":"cmykPress","qualifiedName":"cmykPress","category":"patterns","module":"std/paint/patterns.ts","summary":"A four-color print filter: the layer inside it separated into ink plates, each screened into dots at its own angle and laid down on paper in order.","description":"Goes in `effect:`. `paper` is the color shown where no ink lands. `frequency` is the number of dots down the canvas height. `misprint` offsets each plate in uv (0 is perfect registration, 0.005 is visible fringing), in a direction that turns a quarter turn per plate starting from `misprintAngle` (degrees). `plates` is a list from `inkPlate`; the standard screen angles are cyan 15, magenta 75, yellow 0, black 45. The result keeps the child's alpha.","examples":["effect: cmykPress({paper: p('paperColor'), frequency: p('frequency'), misprint: p('misprint'), misprintAngle: p('misprintAngle'), plates: [inkPlate({channel: 'cyan', screenAngle: p('cyanAngle'), ink: p('cyanColor')}), inkPlate({channel: 'black', screenAngle: p('blackAngle'), ink: p('blackColor')})]})"],"tips":["Any subset of the four plates works. Two plates give a duotone or risograph look."],"see":["patterns.inkPlate","patterns.dotScreen","patterns.dotScreenMask","patterns.inkTransmission"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.cmykPress"},"usedBy":["Halftone"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":938},"kind":"function","signature":"cmykPress(slots: {\n    paper: PropRef\n    frequency: PropRef\n    misprint: PropRef\n    misprintAngle: PropRef\n    plates: InkPlateSpec[]\n}): GatherEffect","params":[{"name":"slots","type":"{\n    paper: PropRef\n    frequency: PropRef\n    misprint: PropRef\n    misprintAngle: PropRef\n    plates: InkPlateSpec[]\n}","optional":false}]},"patterns.chosenBy":{"id":"patterns.chosenBy","name":"chosenBy","qualifiedName":"chosenBy","category":"patterns","module":"std/paint/patterns.ts","summary":"Picks one whole effect recipe from a select prop, decided when the shader compiles.","description":"`prop` must be a `compileTime: true` prop. `read` turns its raw value into one of the keys of `recipes`; accept both the option string and its transformed value. Every recipe must be a gather effect with the same alpha handling and no setup step, or this throws as soon as it is called.","examples":["effect: chosenBy(p('style'), (raw) => (raw === 'cmyk' || raw === 1 ? 'cmyk' : 'classic'), {classic: dotScreen({angle: p('angle'), frequency: p('frequency')}), cmyk: cmykPress({paper: p('paperColor'), frequency: p('frequency'), misprint: p('misprint'), misprintAngle: p('misprintAngle'), plates})})"],"tips":["Changing the prop recompiles the shader, which is the point: each style pays only for its own code."],"see":["patterns.dotScreen","patterns.cmykPress"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.chosenBy"},"usedBy":["Halftone"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":983},"kind":"function","signature":"chosenBy<K extends string>(prop: PropRef, read: (raw: unknown) => K, recipes: Record<K, GatherEffect>): GatherEffect","params":[{"name":"prop","type":"PropRef","optional":false},{"name":"read","type":"(raw: unknown) => K","optional":false},{"name":"recipes","type":"Record<K, GatherEffect>","optional":false}]},"patterns.pixelGrid":{"id":"patterns.pixelGrid","name":"pixelGrid","qualifiedName":"pixelGrid","category":"patterns","module":"std/paint/patterns.ts","summary":"The pixel grid of a dither: the layer inside it chopped into square cells of a given size.","description":"`size` is a prop in pixels of the authored frame, so the cell count stays the same when the canvas renders at a higher resolution. Build it once and pass the same grid to `quantise` and `ditherInks`.","examples":["const grid = pixelGrid({size: p('pixelSize')})"],"tips":[],"see":["patterns.quantise","patterns.ditherInks"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.pixelGrid"},"usedBy":["Dither"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":1035},"kind":"function","signature":"pixelGrid(slots: {size: PropRef}): PixelGrid","params":[{"name":"slots","type":"{size: PropRef}","optional":false}]},"patterns.quantise":{"id":"patterns.quantise","name":"quantise","qualifiedName":"quantise","category":"patterns","module":"std/paint/patterns.ts","summary":"Reduces each grid cell of the layer inside to 0 or 1 using a dither pattern.","description":"Returns a per-pixel level for `ditherInks`. `pattern` is a `compileTime: true` select prop with one of `bayer2`, `bayer4`, `bayer8`, `clusteredDot`, `blueNoise`, `whiteNoise` or `floydSteinberg`. `threshold` 0–1 shifts the cut point; 0.5 is neutral. `spread` 0–1 is how much of the brightness range dithers; lower leaves more solid areas.","examples":["const levels = quantise({grid, pattern: p('pattern'), threshold: p('threshold'), spread: p('spread')})"],"tips":["Floyd–Steinberg is the costliest pattern: it re-reads 64 cells per pixel. Bayer 4 is the cheap default."],"see":["patterns.pixelGrid","patterns.ditherInks"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.quantise"},"usedBy":["Dither"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":1079},"kind":"function","signature":"quantise(slots: {grid: PixelGrid; pattern: PropRef; threshold: PropRef; spread: PropRef}): GatherStage","params":[{"name":"slots","type":"{grid: PixelGrid; pattern: PropRef; threshold: PropRef; spread: PropRef}","optional":false}]},"patterns.ditherInks":{"id":"patterns.ditherInks","name":"ditherInks","qualifiedName":"ditherInks","category":"patterns","module":"std/paint/patterns.ts","summary":"A dither filter: colors the 0/1 levels from `quantise`, either with two colors of your own or with darkened and brightened versions of the layer's own pixels.","description":"Goes in `effect:`. `mode` is a `compileTime: true` select prop, `'custom'` or `'source'`. `colors` is `[dark, light]` for custom mode; a transparent dark color lets the background show through.","examples":["effect: ditherInks({grid, levels: quantise({grid, pattern: p('pattern'), threshold: p('threshold'), spread: p('spread')}), mode: p('colorMode'), colors: [p('colorA'), p('colorB')]})"],"tips":[],"see":["patterns.pixelGrid","patterns.quantise","patterns.dotScreen"],"import":{"statement":"import {patterns} from 'shaders/std'","usage":"patterns.ditherInks"},"usedBy":["Dither"],"documented":true,"source":{"file":"std/paint/patterns.ts","line":1124},"kind":"function","signature":"ditherInks(slots: {\n    grid: PixelGrid\n    levels: GatherStage\n    mode: PropRef\n    colors: [PropRef, PropRef]\n}): GatherEffect","params":[{"name":"slots","type":"{\n    grid: PixelGrid\n    levels: GatherStage\n    mode: PropRef\n    colors: [PropRef, PropRef]\n}","optional":false}]},"figures.strokedSegment":{"id":"figures.strokedSegment","name":"strokedSegment","qualifiedName":"strokedSegment","category":"figures","module":"std/paint/figures.ts","summary":"A straight line between two points, solid, dashed or dotted, with square or round ends.","description":"Dashes and dots are spaced so the pattern lands exactly on both endpoints. The edge stays antialiased at any thickness. Style and cap inputs are numbers, so give a select prop a `transform` that maps its labels to them, or pass the number directly.","examples":["paint: strokedSegment({from: p('pointA'), to: p('pointB'), width: p('thickness'), style: 1, dashLength: p('dashLength'), gapLength: p('gapLength'), capStart: 1, capEnd: 1, color: p('color')})"],"tips":[],"see":["figures.sineStroke"],"import":{"statement":"import {figures} from 'shaders/std'","usage":"figures.strokedSegment"},"usedBy":["Line"],"documented":true,"source":{"file":"std/paint/figures.ts","line":77},"kind":"function","signature":"strokedSegment(slots: StrokedSegmentSlots): Paint","params":[{"name":"slots","type":"StrokedSegmentSlots","optional":false}]},"figures.sineStroke":{"id":"figures.sineStroke","name":"sineStroke","qualifiedName":"sineStroke","category":"figures","module":"std/paint/figures.ts","summary":"A wavy line that travels along itself, drawn as a soft band.","description":"The wave is centered on `position` and turned by `angle`. It moves on the layer's clock, so declare `animatedTime: {speed: 'speed'}`.","examples":["paint: sineStroke({position: p('position'), angle: p('angle'), frequency: p('frequency'), amplitude: p('amplitude'), thickness: p('thickness'), softness: p('softness'), color: p('color')})"],"tips":[],"see":["figures.strokedSegment"],"import":{"statement":"import {figures} from 'shaders/std'","usage":"figures.sineStroke"},"usedBy":["SineWave"],"documented":true,"source":{"file":"std/paint/figures.ts","line":126},"kind":"function","signature":"sineStroke(slots: SineStrokeSlots): Paint","params":[{"name":"slots","type":"SineStrokeSlots","optional":false}]},"compose.layered":{"id":"compose.layered","name":"layered","qualifiedName":"layered","category":"compose","module":"std/paint/compose.ts","summary":"Builds N variations of one thing from a data list and sums them with weights.","description":"Give it a list of plain data and a function that turns one entry into `{value, weight}`. You get `weightedSum` and `totalWeight` back and choose the blend yourself, usually `weightedSum / (totalWeight + 0.001)`. Slice the list to change how many layers are built.","examples":["const {weightedSum, totalWeight} = layered(CURTAINS.slice(0, count), ({offset, weight}) => ({value: curtain(offset), weight: math.mul(alpha, weight)}))"],"tips":["Cheap depth: aurora curtains, cloud banks, echo trails are all a few layers of one recipe."],"see":["compose.layers"],"import":{"statement":"import {compose} from 'shaders/std'","usage":"compose.layered"},"usedBy":["Aurora"],"documented":true,"source":{"file":"std/paint/compose.ts","line":53},"kind":"function","signature":"layered<T>(layerData: readonly T[], build: (layer: T, index: number) => {value: Expr; weight: Expr}): {weightedSum: Expr; totalWeight: Expr}","params":[{"name":"layerData","type":"readonly T[]","optional":false},{"name":"build","type":"(layer: T, index: number) => {value: Expr; weight: Expr}","optional":false}]},"compose.layers":{"id":"compose.layers","name":"layers","qualifiedName":"layers","category":"compose","module":"std/paint/compose.ts","summary":"Stacks colors into one, first entry at the back.","description":"Each entry is a color, or `{paint, blend, opacity, behind}` (see `Layer`). Entries add together unless one asks for `'screen'`. Needs at least one entry.","examples":["const rgb = layers([\n  {paint: sky, opacity: 0.6},\n  {paint: stars, behind: fog.transmittance},\n  {paint: glow, blend: 'screen'},\n])"],"tips":[],"see":["compose.layered","compose.emissiveAlpha"],"import":{"statement":"import {compose} from 'shaders/std'","usage":"compose.layers"},"usedBy":["Nebula"],"documented":true,"source":{"file":"std/paint/compose.ts","line":88},"kind":"function","signature":"layers(entries: Layer[]): Expr","params":[{"name":"entries","type":"Layer[]","optional":false}]},"compose.emissiveAlpha":{"id":"compose.emissiveAlpha","name":"emissiveAlpha","qualifiedName":"emissiveAlpha","category":"compose","module":"std/paint/compose.ts","summary":"Turns an rgb light value into a color with alpha, so glows composite over transparency.","description":"Alpha is the brightest channel and the color is scaled up to match, so over black the layer looks exactly like the light it is, and over anything else like a translucent tint. Use it as the last step of a glow, spill or flare. Not for opaque bodies.","examples":["paint: (params) => compose.emissiveAlpha(math.mul(params.uniforms.color.member('rgb'), coverage))"],"tips":[],"see":["compose.layers","compose.dithered"],"import":{"statement":"import {compose} from 'shaders/std'","usage":"compose.emissiveAlpha"},"usedBy":["Irradiance"],"documented":true,"source":{"file":"std/paint/compose.ts","line":118},"kind":"function","signature":"emissiveAlpha(rgb: Expr, hint: string = 'emissive'): Expr","params":[{"name":"rgb","type":"Expr","optional":false},{"name":"hint","type":"string","optional":true,"defaultValue":"'emissive'"}]},"compose.dithered":{"id":"compose.dithered","name":"dithered","qualifiedName":"dithered","category":"compose","module":"std/paint/compose.ts","summary":"Adds a tiny amount of noise to a final color so long soft gradients show no banding.","description":"Apply it to the finished rgba, never to a value that still feeds a palette.","examples":["paint: (params) => dithered(standardPalette(field, params), params)"],"tips":[],"see":["compose.emissiveAlpha"],"import":{"statement":"import {compose} from 'shaders/std'","usage":"compose.dithered"},"usedBy":["MeshGradient"],"documented":true,"source":{"file":"std/paint/compose.ts","line":135},"kind":"function","signature":"dithered(color: Expr, params: GpuFragmentParams): Expr","params":[{"name":"color","type":"Expr","optional":false},{"name":"params","type":"GpuFragmentParams","optional":false}]},"media.imageMedia":{"id":"media.imageMedia","name":"imageMedia","qualifiedName":"imageMedia","category":"media","module":"std/paint/media.ts","summary":"An image from a URL prop, fitted into the layer.","description":"The layer is transparent until the image loads, then shows it at its real aspect ratio. Set `naturalSizeKey: {fromProp: 'url'}` on the definition so layout can measure the image. `onError` receives load failures with the URL so you can phrase the message.","examples":["...imageMedia({src: p('url'), fit: p('objectFit'), decode: 'srgb-linear-alpha-cut', label: 'Photo'})"],"tips":[],"see":["media.videoMedia","media.webcamMedia"],"import":{"statement":"import {media} from 'shaders/std'","usage":"media.imageMedia"},"usedBy":["ImageTexture"],"documented":true,"source":{"file":"std/paint/media.ts","line":81},"kind":"function","signature":"imageMedia(opts: MediaFitSlots & {\n    /** The URL prop. */\n    src: PropRef\n    /** A debug label for the texture, shown in GPU tooling. */\n    label: string\n    /** Called when the image fails to load. */\n    onError?: (error: unknown, url: string) => void\n}): MediaSurfaceHalf","params":[{"name":"opts","type":"MediaFitSlots & {\n    /** The URL prop. */\n    src: PropRef\n    /** A debug label for the texture, shown in GPU tooling. */\n    label: string\n    /** Called when the image fails to load. */\n    onError?: (error: unknown, url: string) => void\n}","optional":false}]},"media.videoMedia":{"id":"media.videoMedia","name":"videoMedia","qualifiedName":"videoMedia","category":"media","module":"std/paint/media.ts","summary":"A video from a URL prop, playing in the layer.","description":"The video plays muted and inline. `loop` is a boolean prop. `metadataTimeoutMs` is how long to wait for the video's metadata before reporting a failure. Set `naturalSizeKey: {fromProp: 'url'}` on the definition. `onError` tells you whether loading (`'acquire'`) or playback (`'autoplay'`) failed.","examples":["...videoMedia({src: p('url'), fit: p('objectFit'), loop: p('loop'), decode: 'srgb-linear-alpha-cut', metadataTimeoutMs: 10000})"],"tips":[],"see":["media.imageMedia","media.webcamMedia"],"import":{"statement":"import {media} from 'shaders/std'","usage":"media.videoMedia"},"usedBy":["VideoTexture"],"documented":true,"source":{"file":"std/paint/media.ts","line":134},"kind":"function","signature":"videoMedia(opts: MediaFitSlots & {\n    /** The URL prop. */\n    src: PropRef\n    /** The loop prop, synced to the video each frame. */\n    loop: PropRef\n    /** Milliseconds to wait for the video's metadata before giving up. */\n    metadataTimeoutMs: number\n    onError?: (error: unknown, info: {url: string | null; stage: 'acquire' | 'autoplay'}) => void\n}): MediaSurfaceHalf","params":[{"name":"opts","type":"MediaFitSlots & {\n    /** The URL prop. */\n    src: PropRef\n    /** The loop prop, synced to the video each frame. */\n    loop: PropRef\n    /** Milliseconds to wait for the video's metadata before giving up. */\n    metadataTimeoutMs: number\n    onError?: (error: unknown, info: {url: string | null; stage: 'acquire' | 'autoplay'}) => void\n}","optional":false}]},"media.webcamMedia":{"id":"media.webcamMedia","name":"webcamMedia","qualifiedName":"webcamMedia","category":"media","module":"std/paint/media.ts","summary":"The user's camera, live in the layer.","description":"Asks for camera permission with your `constraints`. `mirror` is a boolean prop for a selfie flip that updates instantly. This word also accepts `fit: 'none'` to show the feed at its native pixel size. Set `naturalSizeKey: {fixed: '<key>'}` on the definition and pass the same key here. `onError` receives permission denials and playback failures.","examples":["...webcamMedia({fit: p('objectFit'), mirror: p('mirror'), decode: 'srgb-linear-alpha-cut', constraints: {video: {facingMode: 'user'}}, naturalSizeKey: 'webcam'})"],"tips":[],"see":["media.videoMedia","media.imageMedia"],"import":{"statement":"import {media} from 'shaders/std'","usage":"media.webcamMedia"},"usedBy":["WebcamTexture"],"documented":true,"source":{"file":"std/paint/media.ts","line":174},"kind":"function","signature":"webcamMedia(opts: MediaFitSlots & {\n    /** The selfie-mirror boolean prop. */\n    mirror: PropRef\n    /** The `getUserMedia` constraints to request the camera with. */\n    constraints: MediaStreamConstraints\n    /** The key the definition's `naturalSizeKey: {fixed}` uses (a webcam has no URL). */\n    naturalSizeKey: string\n    onError?: (error: unknown, info: {url: string | null; stage: 'acquire' | 'autoplay'}) => void\n}): MediaSurfaceHalf","params":[{"name":"opts","type":"MediaFitSlots & {\n    /** The selfie-mirror boolean prop. */\n    mirror: PropRef\n    /** The `getUserMedia` constraints to request the camera with. */\n    constraints: MediaStreamConstraints\n    /** The key the definition's `naturalSizeKey: {fixed}` uses (a webcam has no URL). */\n    naturalSizeKey: string\n    onError?: (error: unknown, info: {url: string | null; stage: 'acquire' | 'autoplay'}) => void\n}","optional":false}]},"noise.waves":{"id":"noise.waves","name":"waves","qualifiedName":"waves","category":"noise","module":"std/paint/noise.ts","summary":"Overlapping traveling sine waves from a schedule written as data: the organic wobble behind blob edges, water lines and drifting fills.","description":"Pass the coordinates to wave over (`x`, optionally `y`, and `t` for the layer's clock) and a list of terms. Each term multiplies its waves and the terms add up. The result is signed: one wave runs −1 to 1, a schedule runs within about ±(sum of weights). It returns an expression, not a field, so it slots into `paint: (params) => …` recipes and math.","examples":["const wobble = waves({x: delta.member('x'), y: delta.member('y'), t: time}, [\n  {waves: [{x: 3, t: 0.8}, {y: 2.2, t: -0.5}]},\n  {waves: [{x: 5.1, y: -3.3, t: 0.6}], weight: 0.5},\n])"],"tips":["Keep the schedule as constants next to the definition. It is the look, so keep it where you can tune it."],"see":["noise.wavyLine","noise.organicWaves","math.hashPhase"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.waves"},"usedBy":["Blob","Waveform"],"documented":true,"source":{"file":"std/paint/noise.ts","line":82},"kind":"function","signature":"waves(axes: {x: Expr | number; y?: Expr | number; t?: Expr | number}, schedule: WaveTerm[]): Expr","params":[{"name":"axes","type":"{x: Expr | number; y?: Expr | number; t?: Expr | number}","optional":false},{"name":"schedule","type":"WaveTerm[]","optional":false}]},"noise.wavyLine":{"id":"noise.wavyLine","name":"wavyLine","qualifiedName":"wavyLine","category":"noise","module":"std/paint/noise.ts","summary":"The height of an animated wavy line at a point along it: a stack of traveling sines.","description":"`along` is the coordinate along the line and `time` the layer's clock. Each wave has a `freq` (tightness), a `speed` (drift along the clock), an `amount` (height) and an optional `phase` in radians. Signed, within ±(sum of amounts). Add it to a baseline to get the curve. Give each line of a family its own `hashPhase` so they do not move in step.","examples":["const arc = wavyLine({along: x, time: t, waves: [{freq: 1.4, speed: 0.3, amount: 0.5}, {freq: 3.1, speed: -0.2, amount: 0.2}]})"],"tips":[],"see":["noise.waves","math.hashPhase"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.wavyLine"},"usedBy":["Aurora"],"documented":true,"source":{"file":"std/paint/noise.ts","line":127},"kind":"function","signature":"wavyLine(opts: {\n    along: Expr | number\n    time: Expr | number\n    waves: {freq: number; speed: number; amount: number; phase?: Expr | number}[]\n}): Expr","params":[{"name":"opts","type":"{\n    along: Expr | number\n    time: Expr | number\n    waves: {freq: number; speed: number; amount: number; phase?: Expr | number}[]\n}","optional":false}]},"noise.organicWaves":{"id":"noise.organicWaves","name":"organicWaves","qualifiedName":"organicWaves","category":"noise","module":"std/paint/noise.ts","summary":"A ready-made `waves` schedule from three knobs, for when you have no hand-tuned constants.","description":"`frequency` sets the size of the largest waves. `detail` is how many finer layers stack on top (default 3, each about 1.8× finer and half as strong). `drift` is the animation speed (default 0.5). The same `seed` always gives the same schedule. Weights are normalized so the summed wave stays within about −1 to 1. Treat the result as a starting point to tune.","examples":["const wobble = waves({x: delta.member('x'), y: delta.member('y'), t: time}, organicWaves({frequency: 3, detail: 3, drift: 0.4}))"],"tips":["The schedule uses x, y and t, so the axes you pass to `waves` must provide all three."],"see":["noise.waves"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.organicWaves"},"usedBy":[],"documented":true,"source":{"file":"std/paint/noise.ts","line":154},"kind":"function","signature":"organicWaves(opts: {frequency: number; detail?: number; drift?: number; seed?: number}): WaveTerm[]","params":[{"name":"opts","type":"{frequency: number; detail?: number; drift?: number; seed?: number}","optional":false}]},"noise.foldSet":{"id":"noise.foldSet","name":"foldSet","qualifiedName":"foldSet","category":"noise","module":"std/paint/noise.ts","summary":"Lays out the ridges of a draped cloth: `count` folds spread evenly across `spread`, each nudged sideways, widened and raised by a seeded amount so no two are alike.","description":"`spread` is the distance the folds cover across the drape, in the units you will pass as `across` to `drapedFolds` (the canvas aspect for a full-width curtain). `width` and `height` are the typical ridge size. `jitter` (0–1, default 0.6) is how far a fold may wander from its even slot. The same `seed` always gives the same layout. `count` is a plain number, so drive it from a compile-time prop.","examples":["const folds = foldSet({count: 7, seed: u.seed, spread: aspect, width: u.foldWidth, height: u.foldDepth})"],"tips":["Call it once, outside any height function: the layout is the same at every tap."],"see":["noise.drapedFolds"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.foldSet"},"usedBy":["Satin"],"documented":true,"source":{"file":"std/paint/noise.ts","line":223},"kind":"function","signature":"foldSet(opts: {\n    count: number\n    seed: Expr | number\n    spread: Expr | number\n    width: Expr | number\n    height: Expr | number\n    jitter?: number\n}): Fold[]","params":[{"name":"opts","type":"{\n    count: number\n    seed: Expr | number\n    spread: Expr | number\n    width: Expr | number\n    height: Expr | number\n    jitter?: number\n}","optional":false}]},"noise.drapedFolds":{"id":"noise.drapedFolds","name":"drapedFolds","qualifiedName":"drapedFolds","category":"noise","module":"std/paint/noise.ts","summary":"The height of a draped cloth at a point: rounded ridges running along one direction, each rippling and swaying along its length, gathered where the cloth is pinned.","description":"`coords` come from a `directionFrame`: `along` runs 0 at the pinned edge to 1 at the free edge, `across` is the distance across the drape in the units `foldSet` laid the folds out in. `ripple.waves` is a `wavyLine` wave list each spine wanders by (every fold starts at its own phase) and `ripple.gain` scales it. `sway` moves all the folds together on the clock, like a breeze. `swell` makes each ridge rise and fall along its length so neighbours merge and part. `pinning` (0–1) gathers the folds at the pinned edge: narrow and still there, wide and free at the far edge. `sharpness` (default 2) is how tightly the ridge falls away and `crease` (0–1, default 0) flattens its crest into a plateau that meets the next fold in a tight valley: 0 is a round bell, 1 a flat face with a sharp crease. The result is a height in the units of each fold's `height`. Take its slope with `fdSlope` or `fdCurvature` for relief.","examples":["const cloth = (at: Expr) => drapedFolds(drape.coordsOf(at), folds, {time: t, ripple: {waves: RIPPLE}, sway: {amount: breeze}, pinning: u.pinning})\nconst relief = fdCurvature(cloth, q, 0.004)"],"tips":["Keep the wave list and swell as constants next to the definition. They are the look."],"see":["noise.foldSet","noise.wavyLine","materials.fdSlope"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.drapedFolds"},"usedBy":["Satin"],"documented":true,"source":{"file":"std/paint/noise.ts","line":271},"kind":"function","signature":"drapedFolds(coords: {along: Expr | number; across: Expr | number}, folds: Fold[], opts: DrapeOptions): Expr","params":[{"name":"coords","type":"{along: Expr | number; across: Expr | number}","optional":false},{"name":"folds","type":"Fold[]","optional":false},{"name":"opts","type":"DrapeOptions","optional":false}]},"noise.clothCoords":{"id":"noise.clothCoords","name":"clothCoords","qualifiedName":"clothCoords","category":"noise","module":"std/paint/noise.ts","summary":"Where on the unfolded cloth a point lands: the material coordinates to draw a pattern in, so it follows the folds instead of floating over them.","description":"Takes the same `coords`, `folds` and options as `drapedFolds`. The cloth riding each ridge moves sideways with its spine, and each flank uses up cloth, so a pattern drawn in these coordinates bends with the ripples and bunches on the steep sides of every ridge. `foreshorten` (default 0.25) is how much cloth a flank consumes. Rebuild a point from the result with the drape's frame and hand it to a grain word in place of the screen point.","examples":["const m = clothCoords(drape.coordsOf(q), folds, {...drapeOpts, foreshorten: 0.25})\nconst onCloth = add(mul(drape.tangent, m.along), mul(drape.perp, m.across))\nconst threads = grainNoise(fibre.coordsOf(onCloth), {freq: [12, 420]})"],"tips":["One more pass over the folds per pixel. Call it once and reuse the point."],"see":["noise.drapedFolds","noise.foldSet","materials.grainNoise"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.clothCoords"},"usedBy":["Satin"],"documented":true,"source":{"file":"std/paint/noise.ts","line":299},"kind":"function","signature":"clothCoords(coords: {along: Expr | number; across: Expr | number}, folds: Fold[], opts: DrapeOptions & {foreshorten?: number}): {along: Expr; across: Expr}","params":[{"name":"coords","type":"{along: Expr | number; across: Expr | number}","optional":false},{"name":"folds","type":"Fold[]","optional":false},{"name":"opts","type":"DrapeOptions & {foreshorten?: number}","optional":false}]},"noise.seededPlane":{"id":"noise.seededPlane","name":"seededPlane","qualifiedName":"seededPlane","category":"noise","module":"std/paint/noise.ts","summary":"Frames a field on a zoomable plane: how big the pattern is and where it starts.","description":"Takes the canvas position, corrects it for aspect ratio, zooms it by `scale` and slides it by `seed`, then hands a 2D pattern position to the field inside. `scale` is exponential: each +1 makes the pattern about 2.7× finer, so a slider from −2 to 5 runs from coarse to fine. A different `seed` shows a different part of the pattern. The framing every noise texture opens with.","examples":["paint: rampOver(noiseTone(seededPlane(unitized(gaborGrains({frequency: p('frequency')})), {scale: p('scale'), seed: p('seed')})), stops(p('colorSpace')))"],"tips":["`noiseField` bases take a 3D position. Put `evolving` between `seededPlane` and the basis, even at `rate: 0`, to supply the third axis."],"see":["noise.evolving","noise.pixelGrid","noise.scaledPlane"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.seededPlane"},"usedBy":["BlockNoise","CurlNoise","ErosionNoise","GaborNoise","PerlinNoise","SimplexNoise","WaveletNoise"],"documented":true,"source":{"file":"std/paint/noise.ts","line":414},"kind":"function","signature":"seededPlane(field: Field, slots: {scale: PropRef; seed: PropRef}): Field","params":[{"name":"field","type":"Field","optional":false},{"name":"slots","type":"{scale: PropRef; seed: PropRef}","optional":false}]},"noise.pixelGrid":{"id":"noise.pixelGrid","name":"pixelGrid","qualifiedName":"pixelGrid","category":"noise","module":"std/paint/noise.ts","summary":"Frames a field on the pixel grid: one value per block of `grain` device pixels.","description":"For patterns that live on pixels rather than on the canvas (blue noise, dither). `grain` is the block size in pixels (1 = every pixel), `seed` picks a different block of the pattern. The field inside gets whole-number pixel coordinates.","examples":["paint: rampOver(noiseTone(pixelGrid(blueSpeckle(), {grain: p('grain'), seed: p('seed')}), {contrast: p('contrast'), balance: p('balance')}), stops(p('colorSpace')))"],"tips":[],"see":["noise.blueSpeckle","noise.seededPlane"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.pixelGrid"},"usedBy":["BlueNoise"],"documented":true,"source":{"file":"std/paint/noise.ts","line":453},"kind":"function","signature":"pixelGrid(field: Field, slots: {grain: PropRef; seed: PropRef}): Field","params":[{"name":"field","type":"Field","optional":false},{"name":"slots","type":"{grain: PropRef; seed: PropRef}","optional":false}]},"noise.scaledPlane":{"id":"noise.scaledPlane","name":"scaledPlane","qualifiedName":"scaledPlane","category":"noise","module":"std/paint/noise.ts","summary":"Frames a field on the aspect-corrected canvas plane, zoomed by a plain multiplier.","description":"Unlike `seededPlane`, `scale` is linear: roughly the number of pattern cells across the canvas height. There is no seed. The position is computed once, so a recipe whose parts read it several times pays for it once.","examples":["paint: rampOver(unitized(scaledPlane(waveletBands({detail: p('detail')}), {scale: p('scale')})), stops(p('colorSpace')))"],"tips":[],"see":["noise.seededPlane","noise.cellDistances"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.scaledPlane"},"usedBy":[],"documented":true,"source":{"file":"std/paint/noise.ts","line":477},"kind":"function","signature":"scaledPlane(field: Field, slots: {scale: PropRef}): Field","params":[{"name":"field","type":"Field","optional":false},{"name":"slots","type":"{scale: PropRef}","optional":false}]},"noise.evolving":{"id":"noise.evolving","name":"evolving","qualifiedName":"evolving","category":"noise","module":"std/paint/noise.ts","summary":"Makes a 3D noise basis morph in place over time instead of sliding across the canvas.","description":"Wraps a basis that takes a 3D position (`noiseField`): the 2D pattern position from the framing outside becomes `(x, y, time × rate)`. `rate` multiplies the layer's clock, on top of the definition's `speed` prop. It sits between the framing and the basis.","examples":["paint: rampOver(noiseTone(seededPlane(evolving(noiseField('perlin'), {rate: 0.3}), {scale: p('scale'), seed: p('seed')}), {contrast: p('contrast'), balance: p('balance')}), stops(p('colorSpace')))"],"tips":["The clock is the definition's `animatedTime: {speed: 'speed'}`. Without it nothing moves."],"see":["noise.seededPlane","noise.curlSpeed"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.evolving"},"usedBy":["BlockNoise","PerlinNoise","SimplexNoise"],"documented":true,"source":{"file":"std/paint/noise.ts","line":505},"kind":"function","signature":"evolving(field: Field, opts: {rate: number}): Field","params":[{"name":"field","type":"Field","optional":false},{"name":"opts","type":"{rate: number}","optional":false}]},"noise.gaborGrains":{"id":"noise.gaborGrains","name":"gaborGrains","qualifiedName":"gaborGrains","category":"noise","module":"std/paint/noise.ts","summary":"Oriented sine grains: short striped patches, each facing a random way, like brushed metal or wood grain.","description":"`frequency` is how many stripes fit in one grain (1 to 24, default 8). The stripes slide along their grain on the layer's clock. Signed, −1 to 1: wrap it in `unitized` before a tone word. Takes a 2D position, so frame it with `seededPlane`.","examples":["paint: rampOver(noiseTone(seededPlane(unitized(gaborGrains({frequency: p('frequency')})), {scale: p('scale'), seed: p('seed')}), {contrast: p('contrast'), balance: p('balance')}), stops(p('colorSpace')))"],"tips":[],"see":["noise.waveletBands","noise.unitized"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.gaborGrains"},"usedBy":["GaborNoise"],"documented":true,"source":{"file":"std/paint/noise.ts","line":532},"kind":"function","signature":"gaborGrains(slots: {frequency: PropRef}): Field","params":[{"name":"slots","type":"{frequency: PropRef}","optional":false}]},"noise.waveletBands":{"id":"noise.waveletBands","name":"waveletBands","qualifiedName":"waveletBands","category":"noise","module":"std/paint/noise.ts","summary":"Rotating bands of wavelets: layered ripples that turn against each other, a watery interference look.","description":"`detail` is the frequency ratio between the four layers (1.05 to 2, default 1.24; higher adds finer ripples). The bands slide on the layer's clock. Signed, about −1 to 1: wrap it in `unitized` before a tone word. Takes a 2D position.","examples":["paint: rampOver(noiseTone(seededPlane(unitized(waveletBands({detail: p('detail')})), {scale: p('scale'), seed: p('seed')}), {contrast: p('contrast'), balance: p('balance')}), stops(p('colorSpace')))"],"tips":[],"see":["noise.gaborGrains","noise.unitized"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.waveletBands"},"usedBy":["WaveletNoise"],"documented":true,"source":{"file":"std/paint/noise.ts","line":552},"kind":"function","signature":"waveletBands(slots: {detail: PropRef}): Field","params":[{"name":"slots","type":"{detail: PropRef}","optional":false}]},"noise.blueSpeckle":{"id":"noise.blueSpeckle","name":"blueSpeckle","qualifiedName":"blueSpeckle","category":"noise","module":"std/paint/noise.ts","summary":"Fine, even speckle with no clumps: blue noise, one random value per pixel block.","description":"Still (no animation). Unit range, about 0 to 1, so it feeds `noiseTone` directly. Only makes sense over `pixelGrid`, which gives it the pixel coordinates it expects.","examples":["paint: rampOver(noiseTone(pixelGrid(blueSpeckle(), {grain: p('grain'), seed: p('seed')})), stops(p('colorSpace')))"],"tips":["A good dither or film-grain layer over another shader at low opacity."],"see":["noise.pixelGrid"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.blueSpeckle"},"usedBy":["BlueNoise"],"documented":true,"source":{"file":"std/paint/noise.ts","line":571},"kind":"function","signature":"blueSpeckle(): Field","params":[]},"noise.erosionRidges":{"id":"noise.erosionRidges","name":"erosionRidges","qualifiedName":"erosionRidges","category":"noise","module":"std/paint/noise.ts","summary":"Terrain eroded by water: sharp branching ridges with gullies running off them.","description":"Still (no animation). Signed, centred on 0: wrap it in `unitized` before a tone word. Takes a 2D position, so frame it with `seededPlane`.","examples":["paint: rampOver(noiseTone(seededPlane(unitized(erosionRidges()), {scale: p('scale'), seed: p('seed')}), {contrast: p('contrast'), balance: p('balance')}), stops(p('colorSpace')))"],"tips":[],"see":["noise.unitized","noise.seededPlane"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.erosionRidges"},"usedBy":["ErosionNoise"],"documented":true,"source":{"file":"std/paint/noise.ts","line":588},"kind":"function","signature":"erosionRidges(): Field","params":[]},"noise.curlSpeed":{"id":"noise.curlSpeed","name":"curlSpeed","qualifiedName":"curlSpeed","category":"noise","module":"std/paint/noise.ts","summary":"How fast a swirling flow moves at each point: soft eddies and streams that morph in place.","description":"`rate` multiplies the layer's clock. Never negative and mostly 0 to 1, so it feeds `noiseTone` directly. Takes a 2D position, so frame it with `seededPlane`.","examples":["paint: rampOver(noiseTone(seededPlane(curlSpeed({rate: 0.2}), {scale: p('scale'), seed: p('seed')}), {contrast: p('contrast'), balance: p('balance')}), stops(p('colorSpace')))"],"tips":[],"see":["noise.evolving","noise.noiseTone"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.curlSpeed"},"usedBy":["CurlNoise"],"documented":true,"source":{"file":"std/paint/noise.ts","line":605},"kind":"function","signature":"curlSpeed(opts: {rate: number}): Field","params":[{"name":"opts","type":"{rate: number}","optional":false}]},"noise.scratchStreaks":{"id":"noise.scratchStreaks","name":"scratchStreaks","qualifiedName":"scratchStreaks","category":"noise","module":"std/paint/noise.ts","summary":"Fine hairline scratches, like a scuffed surface, flickering on the layer's clock.","description":"`thickness` widens the bright core of each streak (0.2 to 5, default 1). The value is 0 off a streak and rises well past 1 on one, so send it through `noiseTone`, which clamps. Takes a 2D position. Works in a `paint:` only, not in a compute pass.","examples":["paint: rampOver(noiseTone(seededPlane(scratchStreaks({thickness: p('thickness')}), {scale: p('scale'), seed: p('seed')})), linearPair(p('colorA'), p('colorB')))"],"tips":[],"see":["noise.noiseTone","noise.linearPair"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.scratchStreaks"},"usedBy":["Scratches"],"documented":true,"source":{"file":"std/paint/noise.ts","line":625},"kind":"function","signature":"scratchStreaks(slots: {thickness: PropRef}): Field","params":[{"name":"slots","type":"{thickness: PropRef}","optional":false}]},"noise.unitized":{"id":"noise.unitized","name":"unitized","qualifiedName":"unitized","category":"noise","module":"std/paint/noise.ts","summary":"Turns a signed field (−1 to 1) into a unit field (0 to 1).","description":"Put it directly around a signed basis (`gaborGrains`, `waveletBands`, `erosionRidges`, `noiseField('mx3signed')`) so `noiseTone` and the palette see 0 to 1. Or skip it and use `signedTone`, which takes the signed value itself.","examples":["seededPlane(unitized(gaborGrains({frequency: p('frequency')})), {scale: p('scale'), seed: p('seed')})"],"tips":[],"see":["noise.signedTone","noise.noiseTone"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.unitized"},"usedBy":["ErosionNoise","GaborNoise","WaveletNoise"],"documented":true,"source":{"file":"std/paint/noise.ts","line":647},"kind":"function","signature":"unitized(field: Field): Field","params":[{"name":"field","type":"Field","optional":false}]},"noise.noiseTone":{"id":"noise.noiseTone","name":"noiseTone","qualifiedName":"noiseTone","category":"noise","module":"std/paint/noise.ts","summary":"The standard contrast and balance controls of a noise texture, turning a 0–1 field into the ramp position.","description":"`contrast` steepens the field around mid-grey (0 = unchanged, positive = sharper, negative = flatter). `balance` shifts the whole range (positive = more of the first color). Leave the slots out for a texture without those controls. The result is flipped: where the field is high the ramp shows its first color. Expects a unit field, so put `unitized` around a signed basis first.","examples":["paint: rampOver(noiseTone(seededPlane(evolving(noiseField('perlin'), {rate: 0.3}), {scale: p('scale'), seed: p('seed')}), {contrast: p('contrast'), balance: p('balance')}), stops(p('colorSpace')))"],"tips":["Contrast here is additive: 0 is the identity, not 1. Default the prop to 0."],"see":["noise.signedTone","noise.gainTone","noise.unitized"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.noiseTone"},"usedBy":["BlockNoise","BlueNoise","CurlNoise","ErosionNoise","GaborNoise","PerlinNoise","Scratches","WaveletNoise"],"documented":true,"source":{"file":"std/paint/noise.ts","line":669},"kind":"function","signature":"noiseTone(field: Field, slots?: {contrast?: PropRef; balance?: PropRef}): Field","params":[{"name":"field","type":"Field","optional":false},{"name":"slots","type":"{contrast?: PropRef; balance?: PropRef}","optional":true}]},"noise.signedTone":{"id":"noise.signedTone","name":"signedTone","qualifiedName":"signedTone","category":"noise","module":"std/paint/noise.ts","summary":"Contrast and balance for a signed field (−1 to 1), applied before it is squashed to 0–1.","description":"The same controls as `noiseTone` (`contrast` 0 = unchanged, `balance` shifts), but working on the raw signed value lets high contrast push further before it clips. Flipped like `noiseTone`: a high field shows the first color. Use it in place of `unitized` plus `noiseTone`.","examples":["paint: rampOver(signedTone(seededPlane(evolving(noiseField('mx3signed'), {rate: 0.5}), {scale: p('scale'), seed: p('seed')}), {contrast: p('contrast'), balance: p('balance')}), stops(p('colorSpace')))"],"tips":[],"see":["noise.noiseTone","noise.unitized"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.signedTone"},"usedBy":["SimplexNoise"],"documented":true,"source":{"file":"std/paint/noise.ts","line":694},"kind":"function","signature":"signedTone(field: Field, slots: {contrast: PropRef; balance: PropRef}): Field","params":[{"name":"field","type":"Field","optional":false},{"name":"slots","type":"{contrast: PropRef; balance: PropRef}","optional":false}]},"noise.gainTone":{"id":"noise.gainTone","name":"gainTone","qualifiedName":"gainTone","category":"noise","module":"std/paint/noise.ts","summary":"Contrast as a multiplier and balance as a shift over a 0–1 field, not flipped.","description":"`contrast` 1 leaves the field alone (a slider from 0.25 to 4 works well) and `balance` shifts it. Low field values read the first color. The tone inside `worleyNoise`. Reach for it when a \"1 = normal\" contrast slider fits better than `noiseTone`'s \"0 = normal\".","examples":["paint: rampOver(gainTone(cellFill(cellDistances({scale: p('scale'), seed: p('seed')}), {edgeIntensity: p('edgeIntensity')}), {contrast: p('contrast'), balance: p('balance')}), stops(p('colorSpace')))"],"tips":[],"see":["noise.noiseTone","noise.worleyNoise"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.gainTone"},"usedBy":[],"documented":true,"source":{"file":"std/paint/noise.ts","line":715},"kind":"function","signature":"gainTone(field: Field, slots: {contrast: PropRef; balance: PropRef}): Field","params":[{"name":"field","type":"Field","optional":false},{"name":"slots","type":"{contrast: PropRef; balance: PropRef}","optional":false}]},"noise.linearPair":{"id":"noise.linearPair","name":"linearPair","qualifiedName":"linearPair","category":"noise","module":"std/paint/noise.ts","summary":"A two-color palette mixed in linear RGB, for a definition with no color-space prop.","description":"Reads two color props. Use `pair` or `stops` from paint when the definition has a `colorSpace` prop.","examples":["paint: rampOver(noiseTone(seededPlane(scratchStreaks({thickness: p('thickness')}), {scale: p('scale'), seed: p('seed')})), linearPair(p('colorA'), p('colorB')))"],"tips":[],"see":["paint.pair","paint.stops"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.linearPair"},"usedBy":["Scratches"],"documented":true,"source":{"file":"std/paint/noise.ts","line":736},"kind":"function","signature":"linearPair(a: PropRef, b: PropRef): Palette","params":[{"name":"a","type":"PropRef","optional":false},{"name":"b","type":"PropRef","optional":false}]},"noise.fractalNoise":{"id":"noise.fractalNoise","name":"fractalNoise","qualifiedName":"fractalNoise","category":"noise","module":"std/paint/noise.ts","summary":"A complete fractal-noise paint: layered simplex noise that morphs over time, read through the color ramp.","description":"`angle` rotates the pattern (degrees). `octaves` is how many layers stack (1 to 8, changed live without a recompile), `detail` how much finer each layer is (1 to 4) and `contrast` how much weaker (0.1 to 1). `seed` re-mixes the layers. `space` is the color-space prop. Reads the definition's `colorA`, `colorB` and, when present, `stops` props.","examples":["paint: fractalNoise({angle: p('angle'), detail: p('detail'), contrast: p('contrast'), octaves: p('octaves'), seed: p('seed'), space: p('colorSpace')})"],"tips":["Declare `animatedTime: {speed: 'speed'}` on the definition. The morphing runs on the layer's clock."],"see":["noise.worleyNoise","paint.noiseField"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.fractalNoise"},"usedBy":["FractalNoise"],"documented":true,"source":{"file":"std/paint/noise.ts","line":760},"kind":"function","signature":"fractalNoise(slots: {\n    angle: PropRef\n    detail: PropRef\n    contrast: PropRef\n    octaves: PropRef\n    seed: PropRef\n    space: PropRef\n}): Paint","params":[{"name":"slots","type":"{\n    angle: PropRef\n    detail: PropRef\n    contrast: PropRef\n    octaves: PropRef\n    seed: PropRef\n    space: PropRef\n}","optional":false}]},"noise.worleyNoise":{"id":"noise.worleyNoise","name":"worleyNoise","qualifiedName":"worleyNoise","category":"noise","module":"std/paint/noise.ts","summary":"A complete cellular-noise paint: distances to drifting cell points, read through the color ramp.","description":"`scale` is the number of cells across (1 to 30) and `jitter` how far the points wander from a grid (0 = rigid grid, 1 = random). `mode` picks the distance field: `'f1'` (cells), `'f2'`, `'f2MinusF1'` (edges), `'f1PlusF2'`, `'f1TimesF2'`. `distance` picks the cell shape: `'euclidean'` (round), `'manhattan'` (diamond), `'chebyshev'` (square). `octaves` (1 to 4) stacks finer layers, with `lacunarity` and `persistence` setting how much finer and weaker. `contrast` is a multiplier (1 = unchanged) and `balance` a shift. Low values read the first color. `mode`, `distance` and `octaves` are compile-time props. Reads the definition's `colorA`, `colorB` and, when present, `stops` props.","examples":["paint: worleyNoise({scale: p('scale'), jitter: p('jitter'), lacunarity: p('lacunarity'), persistence: p('persistence'), contrast: p('contrast'), balance: p('balance'), seed: p('seed'), mode: p('mode'), distance: p('distance'), octaves: p('octaves'), space: p('colorSpace')})"],"tips":["Give the `mode` and `distance` props `transformWorleyMode` and `transformWorleyDistance` as their `transform`."],"see":["noise.cellDistances","noise.transformWorleyMode","noise.transformWorleyDistance","noise.fractalNoise"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.worleyNoise"},"usedBy":["WorleyNoise"],"documented":true,"source":{"file":"std/paint/noise.ts","line":805},"kind":"function","signature":"worleyNoise(slots: {\n    scale: PropRef\n    jitter: PropRef\n    lacunarity: PropRef\n    persistence: PropRef\n    contrast: PropRef\n    balance: PropRef\n    seed: PropRef\n    /** Compile-time prop: `'f1' | 'f2' | 'f2MinusF1' | 'f1PlusF2' | 'f1TimesF2'`. */\n    mode: PropRef\n    /** Compile-time prop: `'euclidean' | 'manhattan' | 'chebyshev'`. */\n    distance: PropRef\n    /** Compile-time prop: 1 to 4. */\n    octaves: PropRef\n    space: PropRef\n}): Paint","params":[{"name":"slots","type":"{\n    scale: PropRef\n    jitter: PropRef\n    lacunarity: PropRef\n    persistence: PropRef\n    contrast: PropRef\n    balance: PropRef\n    seed: PropRef\n    /** Compile-time prop: `'f1' | 'f2' | 'f2MinusF1' | 'f1PlusF2' | 'f1TimesF2'`. */\n    mode: PropRef\n    /** Compile-time prop: `'euclidean' | 'manhattan' | 'chebyshev'`. */\n    distance: PropRef\n    /** Compile-time prop: 1 to 4. */\n    octaves: PropRef\n    space: PropRef\n}","optional":false}]},"noise.cellDistances":{"id":"noise.cellDistances","name":"cellDistances","qualifiedName":"cellDistances","category":"noise","module":"std/paint/noise.ts","summary":"Distances from each pixel to its nearest two cell points, as `(d1, d2)`: the raw material for Voronoi fills and borders.","description":"`scale` is the number of cells across and `seed` picks a different set of points. The points drift on the layer's clock. Feed it to `cellFill` or `cellBorders`, and wrap it in `share` when both read it.","examples":["const cells = share(cellDistances({scale: p('scale'), seed: p('seed')}))"],"tips":[],"see":["noise.cellFill","noise.cellBorders","paint.share"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.cellDistances"},"usedBy":["Voronoi"],"documented":true,"source":{"file":"std/paint/noise.ts","line":862},"kind":"function","signature":"cellDistances(slots: {scale: PropRef; seed: PropRef}): Field","params":[{"name":"slots","type":"{scale: PropRef; seed: PropRef}","optional":false}]},"noise.cellFill":{"id":"noise.cellFill","name":"cellFill","qualifiedName":"cellFill","category":"noise","module":"std/paint/noise.ts","summary":"A 0–1 fill for each cell: 0 at the cell's point, 1 at its boundary, whatever the cell size.","description":"`edgeIntensity` (0 to 1) is how far the boundary color reaches into the cell. Takes the `(d1, d2)` field from `cellDistances`. Ramp it directly: the first color lands at the cell centres.","examples":["paint: rampOver(cellFill(cells, {edgeIntensity: p('edgeIntensity')}), stops(p('colorSpace')))"],"tips":[],"see":["noise.cellDistances","noise.cellBorders"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.cellFill"},"usedBy":["Voronoi"],"documented":true,"source":{"file":"std/paint/noise.ts","line":885},"kind":"function","signature":"cellFill(cells: Field, slots: {edgeIntensity: PropRef}): Field","params":[{"name":"cells","type":"Field","optional":false},{"name":"slots","type":"{edgeIntensity: PropRef}","optional":false}]},"noise.cellBorders":{"id":"noise.cellBorders","name":"cellBorders","qualifiedName":"cellBorders","category":"noise","module":"std/paint/noise.ts","summary":"A mask of the lines between cells: 0 on a boundary, 1 inside a cell.","description":"`softness` is the line width (0 to about 0.4). Pass the same `scale` as `cellDistances` so the lines keep their width when the cell count changes. Takes the `(d1, d2)` field. Use it as the `mask` of `borderOverlay`.","examples":["cellBorders(cells, {softness: p('edgeSoftness'), scale: p('scale')})"],"tips":[],"see":["noise.cellDistances","noise.borderOverlay"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.cellBorders"},"usedBy":["Voronoi"],"documented":true,"source":{"file":"std/paint/noise.ts","line":905},"kind":"function","signature":"cellBorders(cells: Field, slots: {softness: PropRef; scale: PropRef}): Field","params":[{"name":"cells","type":"Field","optional":false},{"name":"slots","type":"{softness: PropRef; scale: PropRef}","optional":false}]},"noise.borderOverlay":{"id":"noise.borderOverlay","name":"borderOverlay","qualifiedName":"borderOverlay","category":"noise","module":"std/paint/noise.ts","summary":"Draws a color over a paint wherever a mask falls to 0, keeping the paint's alpha.","description":"`mask` is 1 where the paint shows through and 0 where `color` covers it. The Voronoi border move: fill the cells, then overlay the border lines.","examples":["paint: borderOverlay(rampOver(cellFill(cells, {edgeIntensity: p('edgeIntensity')}), stops(p('colorSpace'))), cellBorders(cells, {softness: p('edgeSoftness'), scale: p('scale')}), {color: p('colorBorder')})"],"tips":[],"see":["noise.cellBorders","noise.cellFill"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.borderOverlay"},"usedBy":["Voronoi"],"documented":true,"source":{"file":"std/paint/noise.ts","line":924},"kind":"function","signature":"borderOverlay(paint: Paint, mask: Field, slots: {color: PropRef}): Paint","params":[{"name":"paint","type":"Paint","optional":false},{"name":"mask","type":"Field","optional":false},{"name":"slots","type":"{color: PropRef}","optional":false}]},"noise.overRgb":{"id":"noise.overRgb","name":"overRgb","qualifiedName":"overRgb","category":"noise","module":"std/paint/noise.ts","summary":"Runs color-only steps over a paint, in order, leaving its alpha alone.","description":"This is how `ribbons` is closed: the ribbons come out in the working color space, `backToP3` brings them to display color and `tonePow` lifts them.","examples":["const strands = ribbons({from: p('start'), to: p('end'), count: p('lineCount'), width: p('lineWidth'), amplitude: p('amplitude'), frequency: p('frequency'), softness: p('softness'), spread: p('spread'), pinEdges: p('pinEdges'), colorScale: p('colorScale'), colorVariance: p('colorVariance')})\npaint: overRgb(strands, backToP3({space: p('colorSpace')}), tonePow())"],"tips":[],"see":["noise.ribbons","noise.backToP3","noise.tonePow"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.overRgb"},"usedBy":["Strands"],"documented":true,"source":{"file":"std/paint/noise.ts","line":952},"kind":"function","signature":"overRgb(paint: Paint, ...stages: RgbStage[]): Paint","params":[{"name":"paint","type":"Paint","optional":false},{"name":"...stages","type":"RgbStage[]","optional":true}]},"noise.backToP3":{"id":"noise.backToP3","name":"backToP3","qualifiedName":"backToP3","category":"noise","module":"std/paint/noise.ts","summary":"Brings `ribbons` color from its working color space back to display color.","description":"`space` is the definition's compile-time color-space prop; leave it out for linear. Put it first in `overRgb`, right after the ribbons.","examples":["paint: overRgb(strands, backToP3({space: p('colorSpace')}), tonePow())"],"tips":[],"see":["noise.overRgb","noise.ribbons"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.backToP3"},"usedBy":["Strands"],"documented":true,"source":{"file":"std/paint/noise.ts","line":973},"kind":"function","signature":"backToP3(slots: {space?: PropRef}): RgbStage","params":[{"name":"slots","type":"{space?: PropRef}","optional":false}]},"noise.tonePow":{"id":"noise.tonePow","name":"tonePow","qualifiedName":"tonePow","category":"noise","module":"std/paint/noise.ts","summary":"A gentle lift of the mid-tones (rgb to the power 0.85), as an `overRgb` step.","description":"","examples":["paint: overRgb(strands, backToP3({space: p('colorSpace')}), tonePow())"],"tips":[],"see":["noise.overRgb"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.tonePow"},"usedBy":["Strands"],"documented":true,"source":{"file":"std/paint/noise.ts","line":990},"kind":"function","signature":"tonePow(): RgbStage","params":[]},"noise.ribbons":{"id":"noise.ribbons","name":"ribbons","qualifiedName":"ribbons","category":"noise","module":"std/paint/noise.ts","summary":"Flowing gradient-colored ribbons between two points: the Strands look.","description":"`from` and `to` are position props. `count` is how many ribbons, `width` and `softness` their thickness and edge (0 to 1), `spread` how far apart they sit (0 to 1), `amplitude` and `frequency` the size and tightness of the wave (0 to 5). `pinEdges` is a boolean prop that gathers the ribbons at both ends. `colorScale` (1 to 6) is how many times the gradient repeats along a ribbon and `colorVariance` (0 to 1) how far each ribbon is offset in it. The gradient comes from the definition's `stops` prop. The color comes out in the working color space, so close it with `overRgb(ribbons(…), backToP3({space}), tonePow())`. The wave runs on `animatedTime: {speed: 'speed'}` and the colors scroll on a second clock, `extraAnimatedTimes: {color: 'colorSpeed'}`. Declare both on the definition.","examples":["paint: overRgb(\n  ribbons({from: p('start'), to: p('end'), count: p('lineCount'), width: p('lineWidth'), amplitude: p('amplitude'), frequency: p('frequency'), softness: p('softness'), spread: p('spread'), pinEdges: p('pinEdges'), colorScale: p('colorScale'), colorVariance: p('colorVariance')}),\n  backToP3({space: p('colorSpace')}),\n  tonePow(),\n)"],"tips":["Add `stops: colorStopsPropConfig()` to the props. The ribbons read the stop list, not `colorA` / `colorB`."],"see":["noise.overRgb","noise.backToP3","noise.tonePow"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.ribbons"},"usedBy":["Strands"],"documented":true,"source":{"file":"std/paint/noise.ts","line":1018},"kind":"function","signature":"ribbons(slots: {\n    from: PropRef\n    to: PropRef\n    count: PropRef\n    width: PropRef\n    amplitude: PropRef\n    frequency: PropRef\n    softness: PropRef\n    spread: PropRef\n    pinEdges: PropRef\n    colorScale: PropRef\n    colorVariance: PropRef\n}): Paint","params":[{"name":"slots","type":"{\n    from: PropRef\n    to: PropRef\n    count: PropRef\n    width: PropRef\n    amplitude: PropRef\n    frequency: PropRef\n    softness: PropRef\n    spread: PropRef\n    pinEdges: PropRef\n    colorScale: PropRef\n    colorVariance: PropRef\n}","optional":false}]},"noise.reliefBases.stone":{"id":"noise.reliefBases.stone","name":"stone","qualifiedName":"reliefBases.stone","category":"noise","module":"std/paint/noise.ts","summary":"Marbled stone height field.","description":"","examples":[],"tips":[],"see":[],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.reliefBases.stone"},"usedBy":["Stone"],"documented":true,"source":{"file":"std/paint/noise.ts","line":1083},"kind":"constant","signature":"reliefBases.stone: { readonly fn: TgpuFn<(p: Vec2f) => F32>; readonly hint: \"stone12\"; }","params":[]},"noise.reliefBases.wool":{"id":"noise.reliefBases.wool","name":"wool","qualifiedName":"reliefBases.wool","category":"noise","module":"std/paint/noise.ts","summary":"Interwoven fibrous fabric height field.","description":"","examples":[],"tips":[],"see":[],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.reliefBases.wool"},"usedBy":["Wool"],"documented":true,"source":{"file":"std/paint/noise.ts","line":1085},"kind":"constant","signature":"reliefBases.wool: { readonly fn: TgpuFn<(p: Vec2f) => F32>; readonly hint: \"wool12\"; }","params":[]},"noise.reliefBases":{"id":"noise.reliefBases","name":"reliefBases","qualifiedName":"reliefBases","category":"noise","module":"std/paint/noise.ts","summary":"The height fields `noiseRelief` accepts: `stone` (marbled stone) and `wool` (interwoven fibres).","description":"","examples":["gpu: {fragment: noiseRelief(reliefBases.wool)}"],"tips":[],"see":["noise.noiseRelief"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.reliefBases"},"usedBy":["Stone","Wool"],"documented":true,"source":{"file":"std/paint/noise.ts","line":1081},"kind":"object","signature":"reliefBases: { stone, wool }","members":["noise.reliefBases.stone","noise.reliefBases.wool"],"params":[]},"noise.noiseRelief":{"id":"noise.noiseRelief","name":"noiseRelief","qualifiedName":"noiseRelief","category":"noise","module":"std/paint/noise.ts","summary":"Embosses the layer inside with a textured surface: the child is slightly distorted, then lit by a height field so it reads as carved stone or woven cloth.","description":"A filter, not a paint. Set it as the definition's fragment with `requiresRTT` and `requiresChild`, and give the definition props named `intensity`, `scale`, `contrast`, `distortion` and `seed`. `basis` is an entry of `reliefBases`.","examples":["export const Stone = defineShader({\n  name: 'Stone',\n  requiresRTT: true,\n  requiresChild: true,\n  props: {intensity: {default: 0.5}, scale: {default: 1}, contrast: {default: 0}, distortion: {default: 0.15}, seed: {default: 0}},\n  gpu: {fragment: noiseRelief(reliefBases.stone)},\n})"],"tips":["The library shaders build those five props with `reliefStylizeProps` from the utilities, which also sets their sliders."],"see":["noise.reliefBases"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.noiseRelief"},"usedBy":["Stone","Wool"],"documented":true,"source":{"file":"std/paint/noise.ts","line":1109},"kind":"function","signature":"noiseRelief(basis: ReliefBasis): (params: GpuFragmentParams) => Expr","params":[{"name":"basis","type":"ReliefBasis","optional":false}]},"noise.transformWorleyMode":{"id":"noise.transformWorleyMode","name":"transformWorleyMode","qualifiedName":"transformWorleyMode","category":"noise","module":"std/paint/noise.ts","summary":"The `transform` for a `worleyNoise` `mode` prop: maps `'f1'`, `'f2'`, `'f2MinusF1'`, `'f1PlusF2'` or `'f1TimesF2'` to the number the paint reads.","description":"","examples":["mode: {default: 'f1', transform: transformWorleyMode, compileTime: true}"],"tips":[],"see":["noise.worleyNoise","noise.transformWorleyDistance"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.transformWorleyMode"},"usedBy":["WorleyNoise"],"documented":true,"source":{"file":"std/paint/noise.ts","line":1127},"kind":"function","signature":"transformWorleyMode(value: string): number","params":[{"name":"value","type":"string","optional":false}]},"noise.transformWorleyDistance":{"id":"noise.transformWorleyDistance","name":"transformWorleyDistance","qualifiedName":"transformWorleyDistance","category":"noise","module":"std/paint/noise.ts","summary":"The `transform` for a `worleyNoise` `distance` prop: maps `'euclidean'`, `'manhattan'` or `'chebyshev'` to the number the paint reads.","description":"","examples":["distance: {default: 'euclidean', transform: transformWorleyDistance, compileTime: true}"],"tips":[],"see":["noise.worleyNoise","noise.transformWorleyMode"],"import":{"statement":"import {noise} from 'shaders/std'","usage":"noise.transformWorleyDistance"},"usedBy":["WorleyNoise"],"documented":true,"source":{"file":"std/paint/noise.ts","line":1138},"kind":"function","signature":"transformWorleyDistance(value: string): number","params":[{"name":"value","type":"string","optional":false}]},"light.rayBands":{"id":"light.rayBands","name":"rayBands","qualifiedName":"rayBands","category":"light","module":"std/paint/light.ts","summary":"Bright bands that travel along a coordinate, for aurora curtains or shimmering rays.","description":"Returns a 0–1 field. `along` is the coordinate the bands cross (a uv axis, or a noise-warped one for organic rays), `frequency` the band count per unit of it, and `time` × `speed` how fast they travel. `sharpness` above 1 narrows the bright bands.","examples":["const rays = rayBands({along: params.ctx.uv.member('x'), frequency: 12, time: animatedTime(params), speed: 0.3, sharpness: 2})"],"tips":["Add a noise field to `along` before passing it and the straight bands turn into curtains."],"see":["light.rayLobes","light.streakBand"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.rayBands"},"usedBy":["Aurora"],"documented":true,"source":{"file":"std/paint/light.ts","line":46},"kind":"function","signature":"rayBands(opts: {\n    along: Expr\n    frequency: Expr | number\n    time: Expr | number\n    speed: Expr | number\n    phase?: Expr | number\n    sharpness: Expr | number\n}): Expr","params":[{"name":"opts","type":"{\n    along: Expr\n    frequency: Expr | number\n    time: Expr | number\n    speed: Expr | number\n    phase?: Expr | number\n    sharpness: Expr | number\n}","optional":false}]},"light.emitterFalloff":{"id":"light.emitterFalloff","name":"emitterFalloff","qualifiedName":"emitterFalloff","category":"light","module":"std/paint/light.ts","summary":"How light spilling from an emitter dims with distance, with a long realistic tail.","description":"Returns 1 at the emitter, softened over `radius`, then fading like real light. `d` and `radius` share a unit (usually aspect-corrected uv). `power` 1 fades like a line of light, `power` 2 like a point. Use it where `glowPoint` looks too much like a soft blob.","examples":["const spill = emitterFalloff(frame.member('x'), {radius: p('radius'), power: 2})"],"tips":[],"see":["light.glowPoint","light.glowSpot"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.emitterFalloff"},"usedBy":[],"documented":true,"source":{"file":"std/paint/light.ts","line":73},"kind":"function","signature":"emitterFalloff(d: Expr, opts: {radius: Expr | number; power: Expr | number}): Expr","params":[{"name":"d","type":"Expr","optional":false},{"name":"opts","type":"{radius: Expr | number; power: Expr | number}","optional":false}]},"light.glowAt":{"id":"light.glowAt","name":"glowAt","qualifiedName":"glowAt","category":"light","module":"std/paint/light.ts","summary":"A soft round glow at a position on the canvas.","description":"Returns a 0–1 field, 1 at the position. `x` and `y` are in uv (0–1, y down), `aspect` is the canvas width over height so the glow stays round, and `sizeSq` is the glow radius squared. Pass several with different positions and sum them with `additive`.","examples":["const glow = glowAt({uv: params.ctx.uv, aspect: params.ctx.aspect, x: 0.5, y: 0.4, sizeSq: 0.02})"],"tips":["The radius goes in squared: a glow 0.15 across the canvas is `sizeSq: 0.0225`."],"see":["light.glowSpot","light.glowPoint","light.radialFrame"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.glowAt"},"usedBy":["StudioBackground"],"documented":true,"source":{"file":"std/paint/light.ts","line":92},"kind":"function","signature":"glowAt(opts: {uv: Expr; aspect: Expr | number; x: Expr | number; y: Expr | number; sizeSq: Expr | number}): Expr","params":[{"name":"opts","type":"{uv: Expr; aspect: Expr | number; x: Expr | number; y: Expr | number; sizeSq: Expr | number}","optional":false}]},"light.domeNormal":{"id":"light.domeNormal","name":"domeNormal","qualifiedName":"domeNormal","category":"light","module":"std/paint/light.ts","summary":"The surface normals of a flat disc treated as a puffy dome, ready to light with `shine`.","description":"`delta` is the offset from the disc center, `dist` its length and `radius` the disc size, all in the same unit. `bulge.tilt` (default 0.2) sets how far the normals lean outward at the rim and `bulge.drop` (default 0.1) how much the dome flattens toward its edge.","examples":["const highlight = shine({normal: domeNormal({delta, dist, radius}), light: vec3(0.3, -0.5, 0.8), gloss: 32})"],"tips":[],"see":["light.shine"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.domeNormal"},"usedBy":["Blob"],"documented":true,"source":{"file":"std/paint/light.ts","line":112},"kind":"function","signature":"domeNormal(opts: {\n    delta: Expr\n    dist: Expr\n    radius: Expr | number\n    bulge?: {tilt?: number; drop?: number}\n}): Expr","params":[{"name":"opts","type":"{\n    delta: Expr\n    dist: Expr\n    radius: Expr | number\n    bulge?: {tilt?: number; drop?: number}\n}","optional":false}]},"light.shine":{"id":"light.shine","name":"shine","qualifiedName":"shine","category":"light","module":"std/paint/light.ts","summary":"A specular highlight on any surface that gives you a normal.","description":"Returns the raw 0–1 highlight, viewed straight on. Multiply it by your intensity and the shape's coverage yourself. `light` is a unit direction toward the light and `gloss` sets the highlight's tightness (8 is broad, 32 is a small hot spot). Works with `domeNormal`, a bevel, or a heightfield slope.","examples":["const highlight = mul(shine({normal, light: vec3(0.3, -0.5, 0.8), gloss: p('gloss')}), mask)"],"tips":[],"see":["light.domeNormal"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.shine"},"usedBy":["Blob"],"documented":true,"source":{"file":"std/paint/light.ts","line":145},"kind":"function","signature":"shine(opts: {normal: Expr; light: Expr; gloss: Expr | number}): Expr","params":[{"name":"opts","type":"{normal: Expr; light: Expr; gloss: Expr | number}","optional":false}]},"light.radialFrame":{"id":"light.radialFrame","name":"radialFrame","qualifiedName":"radialFrame","category":"light","module":"std/paint/light.ts","summary":"The frame for a burst, halo or sweep around a center prop.","description":"Gives each pixel its distance and angle from the center as `vec2(dist, angle)`. Distance is in aspect-corrected uv (0.5 is half the canvas height), the angle in radians. `center` is a position prop. Evaluate parts in it with `withFrame`.","examples":["coverage: withFrame(radialFrame(p('center')), modulate(rayLobes({count: p('rays'), spin: -1, sharpness: 4}), featherMask({radius: p('radius'), feather: 0.5})), 'burst')"],"tips":[],"see":["light.withFrame","light.seamlessRadialFrame","light.beamFrame"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.radialFrame"},"usedBy":["SunBurst"],"documented":true,"source":{"file":"std/paint/light.ts","line":190},"kind":"function","signature":"radialFrame(center: PropRef): LightValue","params":[{"name":"center","type":"PropRef","optional":false}]},"light.seamlessRadialFrame":{"id":"light.seamlessRadialFrame","name":"seamlessRadialFrame","qualifiedName":"seamlessRadialFrame","category":"light","module":"std/paint/light.ts","summary":"The frame `noiseRays` needs: a radial frame around a center prop with no visible seam.","description":"Patterns that repeat around an angle show a line where the angle wraps. This frame carries what a part needs to hide it. Only `noiseRays` reads it. `center` is a position prop.","examples":["coverage: withFrame(seamlessRadialFrame(p('center')), noiseRays({density: p('density'), intensity: p('intensity'), spotty: p('spotty')}))"],"tips":["The other radial parts (`glowPoint`, `rayLobes`, `featherMask`) read `radialFrame`, not this one. Its layout is different."],"see":["light.noiseRays","light.radialFrame"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.seamlessRadialFrame"},"usedBy":["Godrays"],"documented":true,"source":{"file":"std/paint/light.ts","line":211},"kind":"function","signature":"seamlessRadialFrame(center: PropRef): LightValue","params":[{"name":"center","type":"PropRef","optional":false}]},"light.beamFrame":{"id":"light.beamFrame","name":"beamFrame","qualifiedName":"beamFrame","category":"light","module":"std/paint/light.ts","summary":"The frame of a beam that starts at an anchor prop and points at the canvas center.","description":"Gives each pixel its distance along the beam and its sideways offset from it as `vec2(u, v)`, in aspect-corrected uv. Drag the anchor anywhere and the beam still points inward. Built for filters over a child (a light leak inside `screenGlow`), where it reads the child's uv.","examples":["const frame = local(beamFrame(p('position'))(params), 'leakFrame')"],"tips":[],"see":["light.beamBloom","light.beamStreaks","light.screenGlow"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.beamFrame"},"usedBy":["LightLeak"],"documented":true,"source":{"file":"std/paint/light.ts","line":234},"kind":"function","signature":"beamFrame(anchor: PropRef): LightValue","params":[{"name":"anchor","type":"PropRef","optional":false}]},"light.withFrame":{"id":"light.withFrame","name":"withFrame","qualifiedName":"withFrame","category":"light","module":"std/paint/light.ts","summary":"Evaluate a light part in a frame.","description":"This is where a framed light starts: a frame, then the part (or `modulate` of several) drawn in it. Pass a name as the third argument when the part reads the frame more than once, so the frame is computed once per pixel. `modulate` always reads it more than once.","examples":["coverage: withFrame(radialFrame(p('center')), modulate(softRayLobes({count: p('rayCount'), softness: p('softness'), spin: -1}), featherMask({radius: p('radius'), feather: p('feather')})), 'burstFrame')"],"tips":["Without the name, a `modulate` of two parts computes the frame twice."],"see":["light.radialFrame","light.beamFrame","light.modulate"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.withFrame"},"usedBy":["Godrays","SunBurst"],"documented":true,"source":{"file":"std/paint/light.ts","line":254},"kind":"function","signature":"withFrame(frame: LightValue, part: LightPart, hint?: string): LightValue","params":[{"name":"frame","type":"LightValue","optional":false},{"name":"part","type":"LightPart","optional":false},{"name":"hint","type":"string","optional":true}]},"light.modulate":{"id":"light.modulate","name":"modulate","qualifiedName":"modulate","category":"light","module":"std/paint/light.ts","summary":"Multiply light parts drawn in the same frame, such as a mask over a pattern.","description":"","examples":["modulate(rayLobes({count: 12, spin: -1, sharpness: 4}), glowPoint({sharpness: 6}), featherMask({radius: p('radius'), feather: 0.4}))"],"tips":[],"see":["light.withFrame","light.additive"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.modulate"},"usedBy":["SunBurst"],"documented":true,"source":{"file":"std/paint/light.ts","line":271},"kind":"function","signature":"modulate(first: LightPart, ...rest: LightPart[]): LightPart","params":[{"name":"first","type":"LightPart","optional":false},{"name":"...rest","type":"LightPart[]","optional":true}]},"light.rayLobes":{"id":"light.rayLobes","name":"rayLobes","qualifiedName":"rayLobes","category":"light","module":"std/paint/light.ts","summary":"Soft rays fanning out from the center of a radial frame, turning with the layer's clock.","description":"Returns a 0–1 field with `count` rays. `spin` is a plain number that scales the clock: 1 turns counter-clockwise, -1 clockwise, 0 holds still. `sharpness` above 1 narrows the bright rays, below 1 widens them.","examples":["coverage: withFrame(radialFrame(p('center')), rayLobes({count: p('rays'), spin: -1, sharpness: 4}))"],"tips":[],"see":["light.softRayLobes","light.raySpikes","light.radialFrame"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.rayLobes"},"usedBy":[],"documented":true,"source":{"file":"std/paint/light.ts","line":290},"kind":"function","signature":"rayLobes(slots: {count: ArgSpec; spin: number; sharpness: ArgSpec}): LightPart","params":[{"name":"slots","type":"{count: ArgSpec; spin: number; sharpness: ArgSpec}","optional":false}]},"light.softRayLobes":{"id":"light.softRayLobes","name":"softRayLobes","qualifiedName":"softRayLobes","category":"light","module":"std/paint/light.ts","summary":"`rayLobes` with a softness slider instead of a sharpness.","description":"`softness` 0 gives crisp rays and 1 gives wide soft ones. The rays widen without moving. `spin` is a plain number scaling the layer's clock: negative turns clockwise.","examples":["coverage: withFrame(radialFrame(p('center')), softRayLobes({count: p('rayCount'), softness: p('softness'), spin: -1}))"],"tips":[],"see":["light.rayLobes","light.featherMask"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.softRayLobes"},"usedBy":["SunBurst"],"documented":true,"source":{"file":"std/paint/light.ts","line":309},"kind":"function","signature":"softRayLobes(slots: {count: ArgSpec; softness: ArgSpec; spin: number}): LightPart","params":[{"name":"slots","type":"{count: ArgSpec; softness: ArgSpec; spin: number}","optional":false}]},"light.raySpikes":{"id":"light.raySpikes","name":"raySpikes","qualifiedName":"raySpikes","category":"light","module":"std/paint/light.ts","summary":"Thin star spikes with dark gaps between them, like light through a camera aperture.","description":"Returns a 0–1 field with twice `halfCount` spikes, so pass half the spike count you want. `power` from 3 to 8 makes them thinner. The spikes do not turn.","examples":["coverage: withFrame(radialFrame(p('center')), modulate(raySpikes({halfCount: 3, power: 5}), glowPoint({sharpness: 12})))"],"tips":[],"see":["light.rayLobes","light.flareStarburst"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.raySpikes"},"usedBy":[],"documented":true,"source":{"file":"std/paint/light.ts","line":328},"kind":"function","signature":"raySpikes(slots: {halfCount: ArgSpec; power: ArgSpec}): LightPart","params":[{"name":"slots","type":"{halfCount: ArgSpec; power: ArgSpec}","optional":false}]},"light.ringBand":{"id":"light.ringBand","name":"ringBand","qualifiedName":"ringBand","category":"light","module":"std/paint/light.ts","summary":"Three thin colored rings around the center, the rainbow edge of a lens or a halo.","description":"Returns an RGB, not a field. The red ring sits at `center` + `spread`, green at `center`, blue at `center` - `spread`, each `width` wide, all in the frame's distance unit. A positive `spread` puts red outside, as a real lens does.","examples":["const rings = withFrame(radialFrame(p('center')), ringBand({center: p('radius'), spread: 0.01, width: 0.02}))(params)"],"tips":[],"see":["light.flareHalo","light.glowPoint"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.ringBand"},"usedBy":[],"documented":true,"source":{"file":"std/paint/light.ts","line":348},"kind":"function","signature":"ringBand(slots: {center: ArgSpec; spread: ArgSpec; width: ArgSpec}): LightPart","params":[{"name":"slots","type":"{center: ArgSpec; spread: ArgSpec; width: ArgSpec}","optional":false}]},"light.glowPoint":{"id":"light.glowPoint","name":"glowPoint","qualifiedName":"glowPoint","category":"light","module":"std/paint/light.ts","summary":"A soft glow at the center of a radial frame with no hard edge.","description":"Returns a 0–1 field, 1 at the center. `sharpness` is one over the radius squared, so a larger value gives a tighter glow: for a glow that reaches radius `r`, pass `1 / (r * r)`.","examples":["coverage: withFrame(radialFrame(p('center')), glowPoint({sharpness: 8}))"],"tips":["Sharpness is not a radius. Turning it up makes the glow smaller."],"see":["light.featherMask","light.emitterFalloff","light.glowAt"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.glowPoint"},"usedBy":[],"documented":true,"source":{"file":"std/paint/light.ts","line":368},"kind":"function","signature":"glowPoint(slots: {sharpness: ArgSpec}): LightPart","params":[{"name":"slots","type":"{sharpness: ArgSpec}","optional":false}]},"light.featherMask":{"id":"light.featherMask","name":"featherMask","qualifiedName":"featherMask","category":"light","module":"std/paint/light.ts","summary":"A round mask that is solid inside a radius and fades out at its edge.","description":"Returns 1 inside, fading to 0 at `radius` (in the frame's distance unit). `feather` is the fraction of the radius spent fading: 0 is a hard edge, 1 fades all the way from the center. Multiply it with a pattern in `modulate` to give a light an outer edge.","examples":["modulate(rayLobes({count: 12, spin: -1, sharpness: 4}), featherMask({radius: p('radius'), feather: p('feather')}))"],"tips":[],"see":["light.glowPoint","light.modulate"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.featherMask"},"usedBy":["SunBurst"],"documented":true,"source":{"file":"std/paint/light.ts","line":386},"kind":"function","signature":"featherMask(slots: {radius: ArgSpec; feather: ArgSpec}): LightPart","params":[{"name":"slots","type":"{radius: ArgSpec; feather: ArgSpec}","optional":false}]},"light.noiseRays":{"id":"light.noiseRays","name":"noiseRays","qualifiedName":"noiseRays","category":"light","module":"std/paint/light.ts","summary":"Volumetric god rays: shafts of light drifting outward from the center, moving on the layer's clock.","description":"Returns a 0–1 coverage. `density` sets how many shafts, `intensity` (0–1) how visible they are, and `spotty` breaks each shaft into drifting spots along its length. Draw it in `seamlessRadialFrame`, not `radialFrame`.","examples":["coverage: withFrame(seamlessRadialFrame(p('center')), noiseRays({density: p('density'), intensity: p('intensity'), spotty: p('spotty')}))"],"tips":[],"see":["light.seamlessRadialFrame","light.coverageOver"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.noiseRays"},"usedBy":["Godrays"],"documented":true,"source":{"file":"std/paint/light.ts","line":407},"kind":"function","signature":"noiseRays(slots: {density: ArgSpec; intensity: ArgSpec; spotty: ArgSpec}): LightPart","params":[{"name":"slots","type":"{density: ArgSpec; intensity: ArgSpec; spotty: ArgSpec}","optional":false}]},"light.slowDrift":{"id":"light.slowDrift","name":"slowDrift","qualifiedName":"slowDrift","category":"light","module":"std/paint/light.ts","summary":"The slow breathing and drifting signals that keep a light leak alive.","description":"Call the result with a clock and the params. It returns four numbers: `.x` is a brightness gain to multiply your light by (1 with `flicker` at 0, breathing between 0.25 and 1.75 at 1), and the rest are the slow drifts `beamBloom` and `beamStreaks` read. Pass the whole value to them as `drift`. `seed` re-rolls the pattern.","examples":["const drift = local(slowDrift({seed: p('seed'), flicker: p('flicker')})(animatedTime(params), params), 'leakDrift')"],"tips":[],"see":["light.beamBloom","light.beamStreaks","light.beamFrame"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.slowDrift"},"usedBy":["LightLeak"],"documented":true,"source":{"file":"std/paint/light.ts","line":432},"kind":"function","signature":"slowDrift(slots: {seed: ArgSpec; flicker: ArgSpec}): (clock: Expr, params: GpuFragmentParams) => Expr","params":[{"name":"slots","type":"{seed: ArgSpec; flicker: ArgSpec}","optional":false}]},"light.beamBloom":{"id":"light.beamBloom","name":"beamBloom","qualifiedName":"beamBloom","category":"light","module":"std/paint/light.ts","summary":"The soft pool of light at the start of a beam, with a wider faint shoulder around it.","description":"Call the result with a `beamFrame` value, a `slowDrift` value and the params. It returns `vec2(main, shoulder)`, two 0–1 fields you usually add together. `spread` is the pool's size in the frame's unit (aspect-corrected uv), at least 0.05.","examples":["const bloom = beamBloom({spread: p('spread')})(frame, drift, params)\nconst heat = add(mul(bloom.member('x'), 1.25), bloom.member('y'))"],"tips":[],"see":["light.beamFrame","light.slowDrift","light.beamStreaks"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.beamBloom"},"usedBy":["LightLeak"],"documented":true,"source":{"file":"std/paint/light.ts","line":453},"kind":"function","signature":"beamBloom(slots: {spread: ArgSpec}): (frame: Expr, drift: Expr, params: GpuFragmentParams) => Expr","params":[{"name":"slots","type":"{spread: ArgSpec}","optional":false}]},"light.beamStreaks":{"id":"light.beamStreaks","name":"beamStreaks","qualifiedName":"beamStreaks","category":"light","module":"std/paint/light.ts","summary":"Two bright bands crossing a beam further along it, slowly drifting, like light bouncing inside a film camera.","description":"Call the result with a `beamFrame` value, a `slowDrift` value and the params. It returns a field scaled by `strength` (0 turns the bands off). `spread` is the same value you gave `beamBloom`, so the bands sit past the pool.","examples":["const streaks = beamStreaks({spread: p('spread'), strength: p('streaks')})(frame, drift, params)"],"tips":[],"see":["light.beamBloom","light.slowDrift","light.streakBand"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.beamStreaks"},"usedBy":["LightLeak"],"documented":true,"source":{"file":"std/paint/light.ts","line":475},"kind":"function","signature":"beamStreaks(slots: {spread: ArgSpec; strength: ArgSpec}): (frame: Expr, drift: Expr, params: GpuFragmentParams) => Expr","params":[{"name":"slots","type":"{spread: ArgSpec; strength: ArgSpec}","optional":false}]},"light.dithered":{"id":"light.dithered","name":"dithered","qualifiedName":"dithered","category":"light","module":"std/paint/light.ts","summary":"Adds a trace of noise to a heat field so long soft ramps do not show bands.","description":"Pass the summed heat, the pixel's uv and a clock. Use it right before `heatRamp`. This is for a heat field, not a color: use `compose.dithered` for a finished color.","examples":["return heatRamp({hot: p('colorHot'), mid: p('colorMid'), fringe: p('colorFringe')}).taps(dithered(heat, params.ctx.uv, animatedTime(params)), params)"],"tips":[],"see":["light.heatRamp"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.dithered"},"usedBy":["LightLeak"],"documented":true,"source":{"file":"std/paint/light.ts","line":494},"kind":"function","signature":"dithered(heat: Expr, uv: Expr, clock: Expr): Expr","params":[{"name":"heat","type":"Expr","optional":false},{"name":"uv","type":"Expr","optional":false},{"name":"clock","type":"Expr","optional":false}]},"light.glowSpot":{"id":"light.glowSpot","name":"glowSpot","qualifiedName":"glowSpot","category":"light","module":"std/paint/light.ts","summary":"A soft oval glow over an offset you have already scaled, the core of a bloom or a glare.","description":"Returns a 0–1 field. Divide each axis of the offset by its own radius first: `du` and `dv` at different radii make the glow oval. `sharpness` larger gives a tighter glow.","examples":["const pool = glowSpot(div(dx, p('width')), div(dy, p('height')), 2)"],"tips":[],"see":["light.glowAt","light.glowPoint","light.beamBloom"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.glowSpot"},"usedBy":["StudioBackground"],"documented":true,"source":{"file":"std/paint/light.ts","line":513},"kind":"function","signature":"glowSpot(du: Expr, dv: Expr, sharpness: Expr): Expr","params":[{"name":"du","type":"Expr","optional":false},{"name":"dv","type":"Expr","optional":false},{"name":"sharpness","type":"Expr","optional":false}]},"light.starGlint":{"id":"light.starGlint","name":"starGlint","qualifiedName":"starGlint","category":"light","module":"std/paint/light.ts","summary":"A four-point star of light: a round core with a thin ray along each axis, like a glint off glitter or a star in a photograph.","description":"`offset` is the pixel's position relative to the star's centre, as a `vec2`, in whatever unit you measure the star in (CSS pixels suit small glints). Returns the brightness: 1 at the centre, falling off along the rays and faster everywhere else. `core` is the core's sharpness (larger is a smaller dot). `rayLength` and `rayWidth` are falloff rates along and across each ray (smaller rayLength is a longer ray). `rays` is the rays' brightness relative to the core.","examples":["const glint = starGlint(offset, {core: 0.9, rayLength: 0.45, rayWidth: 1.6, rays: 0.25})"],"tips":["Multiply by a `flashes` blink for a sparkle that comes and goes. Set `reach` to half\nthe cell size when the glints sit on a `scatterPoints` grid, so long rays never hit a cell edge."],"see":["light.glowSpot","light.raySpikes","light.flareStarburst"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.starGlint"},"usedBy":["Sparkle"],"documented":true,"source":{"file":"std/paint/light.ts","line":537},"kind":"function","signature":"starGlint(offset: Expr, opts: {\n    core: Expr | number\n    rayLength: Expr | number\n    rayWidth: Expr | number\n    rays: Expr | number\n    /** Optional: the glint fades to nothing by this distance from its centre (measured along the larger axis). */\n    reach?: Expr | number\n}): Expr","params":[{"name":"offset","type":"Expr","optional":false},{"name":"opts","type":"{\n    core: Expr | number\n    rayLength: Expr | number\n    rayWidth: Expr | number\n    rays: Expr | number\n    /** Optional: the glint fades to nothing by this distance from its centre (measured along the larger axis). */\n    reach?: Expr | number\n}","optional":false}]},"light.streakBand":{"id":"light.streakBand","name":"streakBand","qualifiedName":"streakBand","category":"light","module":"std/paint/light.ts","summary":"One soft band across a single coordinate, peaking at `center`.","description":"Returns a 0–1 field, 1 at `center`, fading over `width` on each side. `u`, `center` and `width` share a unit. Multiply or add several for a stack of parallel bands.","examples":["const bar = streakBand(params.ctx.uv.member('y'), 0.5, p('width'))"],"tips":[],"see":["light.rayBands","light.beamStreaks"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.streakBand"},"usedBy":[],"documented":true,"source":{"file":"std/paint/light.ts","line":570},"kind":"function","signature":"streakBand(u: Expr, center: Expr, width: Expr): Expr","params":[{"name":"u","type":"Expr","optional":false},{"name":"center","type":"Expr","optional":false},{"name":"width","type":"Expr","optional":false}]},"light.segmentProject":{"id":"light.segmentProject","name":"segmentProject","qualifiedName":"segmentProject","category":"light","module":"std/paint/light.ts","summary":"Where a pixel falls along a line from `a` to `b`, and how far it is from that line.","description":"Returns `vec2(t, distance)`. `t` runs 0 at `a` to 1 at `b` and is clamped, so the ends are rounded. All three points share a unit. Feed both values to `lightBeam`.","examples":["const seg = segmentProject(uv, p('start'), p('end'))"],"tips":[],"see":["light.lightBeam"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.segmentProject"},"usedBy":[],"documented":true,"source":{"file":"std/paint/light.ts","line":587},"kind":"function","signature":"segmentProject(p: Expr, a: Expr, b: Expr): Expr","params":[{"name":"p","type":"Expr","optional":false},{"name":"a","type":"Expr","optional":false},{"name":"b","type":"Expr","optional":false}]},"light.lightBeam":{"id":"light.lightBeam","name":"lightBeam","qualifiedName":"lightBeam","category":"light","module":"std/paint/light.ts","summary":"The glow of a beam that can be thicker or softer at one end than the other.","description":"Give it the distance from the line and the position along it from `segmentProject`. Thickness and softness blend from `start` to `end` along the beam, in the distance unit. Returns `vec2(colorT, alpha)`: `alpha` is 1 inside the beam and fades to 0 outside, and `colorT` runs 0 at the core to 1 at the edge so you can shade the two differently.","examples":["const beam = lightBeam(seg.member('y'), seg.member('x'), {start: p('startThickness'), end: p('endThickness')}, {start: p('startSoftness'), end: p('endSoftness')})"],"tips":[],"see":["light.segmentProject"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.lightBeam"},"usedBy":[],"documented":true,"source":{"file":"std/paint/light.ts","line":606},"kind":"function","signature":"lightBeam(dist: Expr, t: Expr, thickness: {start: Expr; end: Expr}, softness: {start: Expr; end: Expr}): Expr","params":[{"name":"dist","type":"Expr","optional":false},{"name":"t","type":"Expr","optional":false},{"name":"thickness","type":"{start: Expr; end: Expr}","optional":false},{"name":"softness","type":"{start: Expr; end: Expr}","optional":false}]},"light.flareFrame":{"id":"light.flareFrame","name":"flareFrame","qualifiedName":"flareFrame","category":"light","module":"std/paint/light.ts","summary":"The shared frame of a camera lens flare, which every `flare*` part reads.","description":"`position` is the light's position prop. `intensity` scales the whole flare and `edgeFade` (0–1) dims it as the light nears the canvas edge. Bind it once with a name and hand the same value to each part, then close the stack with `flareComposite`.","examples":["const flare = local(flareFrame({position: p('lightPosition'), intensity: p('intensity'), edgeFade: p('edgeFade')})(params), 'flare')"],"tips":[],"see":["light.flareCore","light.flareGlare","light.flareHalo","light.flareStarburst","light.flareStreak","light.lensGhost","light.flareComposite"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.flareFrame"},"usedBy":["LensFlare"],"documented":true,"source":{"file":"std/paint/light.ts","line":636},"kind":"function","signature":"flareFrame(slots: {position: PropRef; intensity: ArgSpec; edgeFade: ArgSpec}): LightValue","params":[{"name":"slots","type":"{position: PropRef; intensity: ArgSpec; edgeFade: ArgSpec}","optional":false}]},"light.lensGhost":{"id":"light.lensGhost","name":"lensGhost","qualifiedName":"lensGhost","category":"light","module":"std/paint/light.ts","summary":"One ghost disc of a lens flare, the faint circles that line up across from the light.","description":"Call the result once per ghost with the flare frame, a `disc` and a `tint`, and sum the calls with `additive`. `disc` is `[offset, size, brightness, hollow]`: how far along the axis from the light (scaled by `spread`), radius in aspect-corrected uv, brightness, and 0–1 for a solid disc to a ring. `tint` is `[r, g, b, phase]`, the color and a shimmer offset. `chroma` (0–1) adds a rainbow rim. Returns an RGB.","examples":["const ghost = lensGhost({intensity: p('ghostIntensity'), spread: p('ghostSpread'), chroma: p('ghostChroma')})\nconst ghosts = additive(ghost(flare, [0.15, 0.20, 0.30, 0.10], [1.0, 0.95, 0.90, 0.0], params), ghost(flare, [0.30, 0.12, 0.25, 0.65], [1.0, 0.85, 0.55, 2.17], params))"],"tips":[],"see":["light.flareFrame","light.additive","light.flareHalo"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.lensGhost"},"usedBy":["LensFlare"],"documented":true,"source":{"file":"std/paint/light.ts","line":664},"kind":"function","signature":"lensGhost(slots: {intensity: ArgSpec; spread: ArgSpec; chroma: ArgSpec}): (flare: Expr, disc: [number, number, number, number], tint: [number, number, number, number], params: GpuFragmentParams) => Expr","params":[{"name":"slots","type":"{intensity: ArgSpec; spread: ArgSpec; chroma: ArgSpec}","optional":false}]},"light.flareHalo":{"id":"light.flareHalo","name":"flareHalo","qualifiedName":"flareHalo","category":"light","module":"std/paint/light.ts","summary":"The rainbow halo ring of a lens flare, drawn across the canvas from the light.","description":"Call the result with the flare frame and the params. The ring centers on the point opposite the light through the canvas center. `radius` is in aspect-corrected uv, `chroma` (0–1) splits it into colors and `softness` widens it. Returns an RGB.","examples":["flareHalo({intensity: p('haloIntensity'), radius: p('haloRadius'), chroma: p('haloChroma'), softness: p('haloSoftness')})(flare, params)"],"tips":[],"see":["light.flareFrame","light.ringBand","light.lensGhost"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.flareHalo"},"usedBy":["LensFlare"],"documented":true,"source":{"file":"std/paint/light.ts","line":690},"kind":"function","signature":"flareHalo(slots: {intensity: ArgSpec; radius: ArgSpec; chroma: ArgSpec; softness: ArgSpec}): (flare: Expr, params: GpuFragmentParams) => Expr","params":[{"name":"slots","type":"{intensity: ArgSpec; radius: ArgSpec; chroma: ArgSpec; softness: ArgSpec}","optional":false}]},"light.flareStarburst":{"id":"light.flareStarburst","name":"flareStarburst","qualifiedName":"flareStarburst","category":"light","module":"std/paint/light.ts","summary":"The star of thin spikes around the light in a lens flare.","description":"Call the result with the flare frame and the params. `points` is the number of spikes. They turn slowly with the layer's clock and carry a faint rainbow tint. Returns an RGB.","examples":["flareStarburst({intensity: p('starburstIntensity'), points: p('starburstPoints')})(flare, params)"],"tips":[],"see":["light.flareFrame","light.raySpikes"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.flareStarburst"},"usedBy":["LensFlare"],"documented":true,"source":{"file":"std/paint/light.ts","line":714},"kind":"function","signature":"flareStarburst(slots: {intensity: ArgSpec; points: ArgSpec}): (flare: Expr, params: GpuFragmentParams) => Expr","params":[{"name":"slots","type":"{intensity: ArgSpec; points: ArgSpec}","optional":false}]},"light.flareStreak":{"id":"light.flareStreak","name":"flareStreak","qualifiedName":"flareStreak","category":"light","module":"std/paint/light.ts","summary":"The horizontal widescreen streak through the light in a lens flare.","description":"Call the result with the flare frame and the params. `length` is the streak's reach in uv. Blue spreads furthest, giving the streak its cyan edges. Returns an RGB.","examples":["flareStreak({intensity: p('streakIntensity'), length: p('streakLength')})(flare, params)"],"tips":[],"see":["light.flareFrame","light.streakBand"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.flareStreak"},"usedBy":["LensFlare"],"documented":true,"source":{"file":"std/paint/light.ts","line":736},"kind":"function","signature":"flareStreak(slots: {intensity: ArgSpec; length: ArgSpec}): (flare: Expr, params: GpuFragmentParams) => Expr","params":[{"name":"slots","type":"{intensity: ArgSpec; length: ArgSpec}","optional":false}]},"light.flareGlare":{"id":"light.flareGlare","name":"flareGlare","qualifiedName":"flareGlare","category":"light","module":"std/paint/light.ts","summary":"The broad warm-white wash around the light that lifts contrast near it.","description":"Call the result with the flare frame and the params. `size` is the wash radius in aspect-corrected uv. Returns an RGB.","examples":["flareGlare({intensity: p('glareIntensity'), size: p('glareSize')})(flare, params)"],"tips":[],"see":["light.flareFrame","light.flareCore"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.flareGlare"},"usedBy":["LensFlare"],"documented":true,"source":{"file":"std/paint/light.ts","line":758},"kind":"function","signature":"flareGlare(slots: {intensity: ArgSpec; size: ArgSpec}): (flare: Expr, params: GpuFragmentParams) => Expr","params":[{"name":"slots","type":"{intensity: ArgSpec; size: ArgSpec}","optional":false}]},"light.flareCore":{"id":"light.flareCore","name":"flareCore","qualifiedName":"flareCore","category":"light","module":"std/paint/light.ts","summary":"The bright point at the light itself: a hard pinpoint inside a small soft glow.","description":"Call the result with the flare frame and the params. Returns an RGB.","examples":["flareCore({intensity: p('intensity')})(flare, params)"],"tips":[],"see":["light.flareFrame","light.flareGlare"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.flareCore"},"usedBy":["LensFlare"],"documented":true,"source":{"file":"std/paint/light.ts","line":777},"kind":"function","signature":"flareCore(slots: {intensity: ArgSpec}): (flare: Expr, params: GpuFragmentParams) => Expr","params":[{"name":"slots","type":"{intensity: ArgSpec}","optional":false}]},"light.flareComposite":{"id":"light.flareComposite","name":"flareComposite","qualifiedName":"flareComposite","category":"light","module":"std/paint/light.ts","summary":"Closes a lens flare: turns the summed flare parts into the color to return from `paint`.","description":"Pass the `additive` sum of the parts and the flare frame. The frame's intensity, edge fade and shimmer are applied here, and alpha comes from how bright the flare is.","examples":["return flareComposite(additive(ghosts, halo, starburst, streak, glare, core), flare)"],"tips":[],"see":["light.additive","light.flareFrame"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.flareComposite"},"usedBy":["LensFlare"],"documented":true,"source":{"file":"std/paint/light.ts","line":794},"kind":"function","signature":"flareComposite(total: Expr, flare: Expr): Expr","params":[{"name":"total","type":"Expr","optional":false},{"name":"flare","type":"Expr","optional":false}]},"light.vignetteMask":{"id":"light.vignetteMask","name":"vignetteMask","qualifiedName":"vignetteMask","category":"light","module":"std/paint/light.ts","summary":"Darkens the corners of the canvas.","description":"Returns a field that is 1 at the center and falls toward 0 in the corners, kept round on any canvas shape. Multiply your color by it. `strength` 0 leaves the image alone and about 2 takes the corners to black. `scale` multiplies `strength`, for mapping a 0–100 slider onto that range.","examples":["const vig = vignetteMask({strength: p('vignette'), scale: 0.025})(params)\nreturn vec4(mul(rgb, vig), 1)"],"tips":[],"see":["light.featherMask"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.vignetteMask"},"usedBy":["StudioBackground"],"documented":true,"source":{"file":"std/paint/light.ts","line":816},"kind":"function","signature":"vignetteMask(slots: {strength: ArgSpec; scale?: number}): LightValue","params":[{"name":"slots","type":"{strength: ArgSpec; scale?: number}","optional":false}]},"light.additive":{"id":"light.additive","name":"additive","qualifiedName":"additive","category":"light","module":"std/paint/light.ts","summary":"Adds lights together, since light from several sources simply sums.","description":"","examples":["const total = additive(glare, halo, core)"],"tips":[],"see":["light.modulate","light.flareComposite"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.additive"},"usedBy":["LensFlare"],"documented":true,"source":{"file":"std/paint/light.ts","line":835},"kind":"function","signature":"additive(first: Expr, ...rest: Expr[]): Expr","params":[{"name":"first","type":"Expr","optional":false},{"name":"...rest","type":"Expr[]","optional":true}]},"light.heatRamp":{"id":"light.heatRamp","name":"heatRamp","qualifiedName":"heatRamp","category":"light","module":"std/paint/light.ts","summary":"The colors of overexposed light, from a dim fringe through a mid tone to a hot core.","description":"Pass three color props. Call `.taps(heat, params)` on the result to turn a heat field into RGB: heat 0 is black, the fringe color shows first, the mid color by 0.5, the hot color above 1, and the light keeps getting brighter up to a heat of 1.6. Each channel reads a slightly different heat, which gives bright edges their color fringing.","examples":["return heatRamp({hot: p('colorHot'), mid: p('colorMid'), fringe: p('colorFringe')}).taps(heat, params)"],"tips":["Run the heat through `dithered` first on long soft ramps."],"see":["light.dithered","light.screenGlow"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.heatRamp"},"usedBy":["LightLeak"],"documented":true,"source":{"file":"std/paint/light.ts","line":854},"kind":"function","signature":"heatRamp(colors: {hot: PropRef; mid: PropRef; fringe: PropRef}): { kind: \"heatRamp\"; taps: (heat: Expr, params: { uniforms: Record<string, Expr>; }) => Expr; }","params":[{"name":"colors","type":"{hot: PropRef; mid: PropRef; fringe: PropRef}","optional":false}]},"light.coverageMix":{"id":"light.coverageMix","name":"coverageMix","qualifiedName":"coverageMix","category":"light","module":"std/paint/light.ts","summary":"Blends from a background color to a light color by a coverage field, as a finished paint.","description":"`color` and `background` are color props, `coverage` a 0–1 light such as a `withFrame` result. Both the color and the alpha blend, so a transparent background gives a light that fades to nothing. Pass a `hint` name when the coverage is expensive. Use `coverageOver` when you want real layer compositing instead of a plain blend.","examples":["paint: coverageMix({color: p('color'), background: p('background'), coverage: withFrame(radialFrame(p('center')), glowPoint({sharpness: 6}), 'glow'), hint: 'glowAlpha'})"],"tips":[],"see":["light.coverageOver","light.withFrame"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.coverageMix"},"usedBy":["SunBurst"],"documented":true,"source":{"file":"std/paint/light.ts","line":886},"kind":"function","signature":"coverageMix(slots: {color: PropRef; background: PropRef; coverage: LightValue; hint?: string}): LightValue","params":[{"name":"slots","type":"{color: PropRef; background: PropRef; coverage: LightValue; hint?: string}","optional":false}]},"light.coverageOver":{"id":"light.coverageOver","name":"coverageOver","qualifiedName":"coverageOver","category":"light","module":"std/paint/light.ts","summary":"Lays a light color over a background color by a coverage field, like a layer with an opacity mask, as a finished paint.","description":"`color` and `background` are color props, `coverage` a 0–1 light. The color's own alpha counts as opacity, so a half-transparent ray color shows the background through it.","examples":["paint: coverageOver({color: p('rayColor'), background: p('backgroundColor'), coverage: withFrame(seamlessRadialFrame(p('center')), noiseRays({density: p('density'), intensity: p('intensity'), spotty: p('spotty')}))})"],"tips":[],"see":["light.coverageMix","light.noiseRays"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.coverageOver"},"usedBy":["Godrays"],"documented":true,"source":{"file":"std/paint/light.ts","line":911},"kind":"function","signature":"coverageOver(slots: {color: PropRef; background: PropRef; coverage: LightValue}): LightValue","params":[{"name":"slots","type":"{color: PropRef; background: PropRef; coverage: LightValue}","optional":false}]},"light.screenGlow":{"id":"light.screenGlow","name":"screenGlow","qualifiedName":"screenGlow","category":"light","module":"std/paint/light.ts","summary":"An `effect` that shines emitted light onto the layer inside it, the way light hits film.","description":"Pass a function that builds the light's RGB from the params. The light is screened over the child, pushed toward overexposure where it is bright, and shows even over transparent areas. Build the light with `beamFrame`, `beamBloom`, `beamStreaks` and `heatRamp`.","examples":["effect: screenGlow((params) => heatRamp({hot: p('colorHot'), mid: p('colorMid'), fringe: p('colorFringe')}).taps(heat, params))"],"tips":[],"see":["light.heatRamp","light.beamFrame"],"import":{"statement":"import {light} from 'shaders/std'","usage":"light.screenGlow"},"usedBy":["LightLeak"],"documented":true,"source":{"file":"std/paint/light.ts","line":931},"kind":"function","signature":"screenGlow(glow: (params: FilterParams) => Expr): PointwiseEffect","params":[{"name":"glow","type":"(params: FilterParams) => Expr","optional":false}]},"materials.guarded":{"id":"materials.guarded","name":"guarded","qualifiedName":"guarded","category":"materials","module":"std/paint/materials.ts","summary":"Evaluates an expression only where a condition holds, and a cheap fallback everywhere else.","description":"Use it to skip a material's whole body for pixels outside the shape. Any value you build inside `inner` and also read after the guard must be listed in `deps`.","examples":["return guarded(insideShape(field.sdf, field.pxH), vec4(rgb, silhouette(field, u.edgeSoftness)), vec4(0, 0, 0, 0), 'plastic')"],"tips":["Values first made inside the guard are not visible after it. Put shared locals in `deps`."],"see":["materials.insideShape","materials.silhouette"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.guarded"},"usedBy":["BrushedMetal","CarbonFiber","Chrome","Frost","Goo","Hologram","Holographic","Irradiance","LiquidMetal","Nebula","Obsidian","Plastic","Water"],"documented":true,"source":{"file":"std/paint/materials.ts","line":85},"kind":"function","signature":"guarded(cond: Expr, inner: Expr, fallback: Expr, hint: string = 'guarded', deps: Expr[] = []): Expr","params":[{"name":"cond","type":"Expr","optional":false},{"name":"inner","type":"Expr","optional":false},{"name":"fallback","type":"Expr","optional":false},{"name":"hint","type":"string","optional":true,"defaultValue":"'guarded'"},{"name":"deps","type":"Expr[]","optional":true,"defaultValue":"[]"}]},"materials.insideShape":{"id":"materials.insideShape","name":"insideShape","qualifiedName":"insideShape","category":"materials","module":"std/paint/materials.ts","summary":"True inside the shape and up to two pixels outside it.","description":"Pass the signed distance and pixel size from `surfaceField`. The two-pixel margin keeps the anti-aliased edge inside the shaded region. The usual condition for `guarded`.","examples":["guarded(insideShape(field.sdf, field.pxH), shaded, vec4(0, 0, 0, 0))"],"tips":[],"see":["materials.guarded","materials.silhouette"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.insideShape"},"usedBy":["BrushedMetal","CarbonFiber","Chrome","Frost","Hologram","Holographic","Irradiance","LiquidMetal","Nebula","Obsidian","Plastic","Water"],"documented":true,"source":{"file":"std/paint/materials.ts","line":131},"kind":"function","signature":"insideShape(sdf: Expr, pxH: Expr): Expr","params":[{"name":"sdf","type":"Expr","optional":false},{"name":"pxH","type":"Expr","optional":false}]},"materials.shapedSurface":{"id":"materials.shapedSurface","name":"shapedSurface","qualifiedName":"shapedSurface","category":"materials","module":"std/paint/materials.ts","summary":"Turns a shape prop into a surface your material shades.","description":"Spread the result into a definition that declares the `center`, `scale`, `rotation` and `shape` props (copy them from any shape effect). Your `surface` function receives a `SurfaceFrame` and returns the color at this pixel. Flat shapes, SVG shapes and 3D shapes all arrive through the same frame.","examples":["...shapedSurface({\n  pattern: 'raw',\n  surface: (frame, params) => {\n    const u = params.uniforms\n    const field = surfaceField(frame, params, {scale: u.scale})\n    const n = geometricNormal(frame, field, {bevelWidth: u.bevelWidth, bevelShape: u.bevelShape})\n    const key = keyLightAt(direction(u.lightAngle), -0.7)\n    const rgb = mul(u.color.member('rgb'), lambert(n, key.member('L'), {wrap: 0.3}))\n    return guarded(insideShape(field.sdf, field.pxH), vec4(rgb, silhouette(field, u.edgeSoftness)), vec4(0, 0, 0, 0))\n  },\n}),"],"tips":["Start from `surfaceField`, end with `guarded` around `insideShape`. Everything between is the look."],"see":["materials.surfaceField","materials.geometricNormal","materials.silhouette","materials.guarded"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.shapedSurface"},"usedBy":["BrushedMetal","CarbonFiber","Chrome","Crystal","Emboss","Frost","Glass","Goo","Heatmap","Hologram","Holographic","Irradiance","LightEdge","LiquidMetal","Nebula","Neon","Obsidian","Plastic","ThinFilm","Water"],"documented":true,"source":{"file":"std/paint/materials.ts","line":237},"kind":"function","signature":"shapedSurface(spec: ShapedSurfaceSpec): ShapedSurfaceEffect","params":[{"name":"spec","type":"ShapedSurfaceSpec","optional":false}]},"materials.surfaceField":{"id":"materials.surfaceField","name":"surfaceField","qualifiedName":"surfaceField","category":"materials","module":"std/paint/materials.ts","summary":"The first line of a material: the field taps, the signed distance and the pixel size, bound once.","description":"Pass `scale` (the shape's scale prop) to divide the distance by it, as the library's metals and plastics do, so edge softness and pixel tests track the shape's size. Leave it out for `continuedField`.","examples":["const field = surfaceField(frame, params, {scale: params.uniforms.scale})"],"tips":[],"see":["materials.shapedSurface","materials.geometricNormal","materials.silhouette","materials.insideShape"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.surfaceField"},"usedBy":["BrushedMetal","CarbonFiber","Chrome","Crystal","Frost","Holographic","Irradiance","LiquidMetal","Nebula","Obsidian","Plastic","Water"],"documented":true,"source":{"file":"std/paint/materials.ts","line":330},"kind":"function","signature":"surfaceField(frame: SurfaceFrame, params: GpuFragmentParams, opts: {scale?: Expr} = {}): SurfaceField","params":[{"name":"frame","type":"SurfaceFrame","optional":false},{"name":"params","type":"GpuFragmentParams","optional":false},{"name":"opts","type":"{scale?: Expr}","optional":true,"defaultValue":"{}"}]},"materials.geometricNormal":{"id":"materials.geometricNormal","name":"geometricNormal","qualifiedName":"geometricNormal","category":"materials","module":"std/paint/materials.ts","summary":"The surface normal of the shape, whichever kind of shape is active.","description":"A 3D shape takes its normal from the marched depth. A flat shape takes a normal that tilts away from the face near the edge, over `bevelWidth` (in the units of `field.sdf`) with the profile `bevelShape` (0 round, 1 machined). `sharpness` clamps the 3D slope (default 5, lower is calmer). `eps` must match the stencil step (default 0.01). Needs a stencil with neighbour taps.","examples":["const n = geometricNormal(frame, field, {bevelWidth: u.bevelWidth, bevelShape: u.bevelShape})"],"tips":["Add relief with `nudgeNormal` or `tiltNormal` after this, never before."],"see":["materials.marchedNormal","materials.nudgeNormal","materials.tiltNormal","materials.fieldSlope"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.geometricNormal"},"usedBy":["BrushedMetal","CarbonFiber","Irradiance","LiquidMetal","Nebula","Obsidian"],"documented":true,"source":{"file":"std/paint/materials.ts","line":365},"kind":"function","signature":"geometricNormal(frame: SurfaceFrame, field: SurfaceField, opts: {bevelWidth: Expr; bevelShape: Expr; sharpness?: number; eps?: number; hint?: string}): Expr","params":[{"name":"frame","type":"SurfaceFrame","optional":false},{"name":"field","type":"SurfaceField","optional":false},{"name":"opts","type":"{bevelWidth: Expr; bevelShape: Expr; sharpness?: number; eps?: number; hint?: string}","optional":false}]},"materials.surfacePattern":{"id":"materials.surfacePattern","name":"surfacePattern","qualifiedName":"surfacePattern","category":"materials","module":"std/paint/materials.ts","summary":"Coordinates for a pattern that sticks to the shape's surface.","description":"On a flat shape they are the placement coordinates centred on the shape (0,0 at its centre). On a 3D shape they follow the surface as it rotates, when the effect declares `pattern: 'raw'` or `'triplanar'`.","examples":["const pat = surfacePattern(frame, field)"],"tips":[],"see":["materials.shapedSurface","materials.rotationSensor","materials.surfaceNoiseAt"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.surfacePattern"},"usedBy":["BrushedMetal","CarbonFiber","Chrome","Crystal","Frost","Holographic","LiquidMetal","Obsidian","Plastic","Water"],"documented":true,"source":{"file":"std/paint/materials.ts","line":403},"kind":"function","signature":"surfacePattern(frame: SurfaceFrame, field: SurfaceField, opts: {uv?: Expr} = {}): Expr","params":[{"name":"frame","type":"SurfaceFrame","optional":false},{"name":"field","type":"SurfaceField","optional":false},{"name":"opts","type":"{uv?: Expr}","optional":true,"defaultValue":"{}"}]},"materials.fieldSlope":{"id":"materials.fieldSlope","name":"fieldSlope","qualifiedName":"fieldSlope","category":"materials","module":"std/paint/materials.ts","summary":"The direction the shape's distance grows in: a 2D vector pointing outward at this pixel.","description":"Its length is about 1 on a flat shape. `eps` is the tap step (default 0.01) and must match the stencil step.","examples":["const grad = fieldSlope(field)"],"tips":[],"see":["materials.nearestEdge","materials.geometricNormal","materials.tiltAlong"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.fieldSlope"},"usedBy":["Crystal","Frost","Holographic","Irradiance","Plastic","Water"],"documented":true,"source":{"file":"std/paint/materials.ts","line":419},"kind":"function","signature":"fieldSlope(field: SurfaceField, eps: number = 0.01): Expr","params":[{"name":"field","type":"SurfaceField","optional":false},{"name":"eps","type":"number","optional":true,"defaultValue":"0.01"}]},"materials.viewRay":{"id":"materials.viewRay","name":"viewRay","qualifiedName":"viewRay","category":"materials","module":"std/paint/materials.ts","summary":"The direction the viewer looks along at this pixel, for reflections that sweep across a face.","description":"A unit 3D vector pointing into the canvas. `fov` sets how much it leans off-axis toward the edges (0.6 is the usual value, 0.55 flatter).","examples":["const view = viewRay(params, field)"],"tips":[],"see":["materials.grazingOf","math.reflect","materials.studioSoftboxes"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.viewRay"},"usedBy":["BrushedMetal","CarbonFiber","Chrome","LiquidMetal","Nebula","Obsidian","Satin"],"documented":true,"source":{"file":"std/paint/materials.ts","line":435},"kind":"function","signature":"viewRay(params: GpuFragmentParams, field: Pick<SurfaceField, 'aspect'>, fov: number = 0.6): Expr","params":[{"name":"params","type":"GpuFragmentParams","optional":false},{"name":"field","type":"Pick<SurfaceField, 'aspect'>","optional":false},{"name":"fov","type":"number","optional":true,"defaultValue":"0.6"}]},"materials.grazingOf":{"id":"materials.grazingOf","name":"grazingOf","qualifiedName":"grazingOf","category":"materials","module":"std/paint/materials.ts","summary":"How edge-on the surface is to the viewer: 0 face-on, 1 grazing.","description":"The input the fresnel words take when you have a view ray. Without one, use `grazingFlat`.","examples":["const grazing = grazingOf(n, viewRay(params, field))"],"tips":[],"see":["materials.grazingFlat","materials.fresnelBoost","materials.schlickFresnel"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.grazingOf"},"usedBy":["BrushedMetal","CarbonFiber","Chrome","Obsidian","Voxels"],"documented":true,"source":{"file":"std/paint/materials.ts","line":450},"kind":"function","signature":"grazingOf(n: Expr, view: Expr): Expr","params":[{"name":"n","type":"Expr","optional":false},{"name":"view","type":"Expr","optional":false}]},"materials.tiltNormal":{"id":"materials.tiltNormal","name":"tiltNormal","qualifiedName":"tiltNormal","category":"materials","module":"std/paint/materials.ts","summary":"Tilts a normal along and across a direction, for brushed or woven relief.","description":"`frame` is a `directionFrame` (from `frames`). `along` and `across` are slope amounts; a few tenths is a visible relief.","examples":["const n = tiltNormal(nGeo, brush, {across: reliefAcross, along: reliefAlong})"],"tips":[],"see":["materials.nudgeNormal","materials.tiltAlong","materials.grainNoise"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.tiltNormal"},"usedBy":["BrushedMetal","Satin"],"documented":true,"source":{"file":"std/paint/materials.ts","line":466},"kind":"function","signature":"tiltNormal(n: Expr, frame: DirectionFrame, slopes: {along?: Expr; across?: Expr}, hint: string = 'n'): Expr","params":[{"name":"n","type":"Expr","optional":false},{"name":"frame","type":"DirectionFrame","optional":false},{"name":"slopes","type":"{along?: Expr; across?: Expr}","optional":false},{"name":"hint","type":"string","optional":true,"defaultValue":"'n'"}]},"materials.studioSoftboxes":{"id":"materials.studioSoftboxes","name":"studioSoftboxes","qualifiedName":"studioSoftboxes","category":"materials","module":"std/paint/materials.ts","summary":"The brightness of a five-light studio seen in a reflected direction.","description":"Pass the x and y of a reflected view ray. `rotation` is a `direction()` vector that turns the studio. `keyRadius` sizes the main light (0.16 crisp, 0.42 soft), `drift` slides it, `strength` scales the whole bank and `skyGain` the floor-to-sky gradient behind it (0.3–0.4).","examples":["const env = studioSoftboxes(R.member('x'), R.member('y'), {rotation: direction(u.envRotation), keyRadius: 0.3, drift: 0, strength: u.environment, skyGain: 0.38})"],"tips":["Smear it with `smearAlong` for a brushed look."],"see":["materials.smearAlong","math.reflect","materials.viewRay"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.studioSoftboxes"},"usedBy":["BrushedMetal","CarbonFiber","Nebula","Obsidian"],"documented":true,"source":{"file":"std/paint/materials.ts","line":494},"kind":"function","signature":"studioSoftboxes(x: Expr, y: Expr, opts: {\n    /** A `direction()` vector that rotates the studio. */\n    rotation: Expr\n    keyRadius: Expr | number\n    drift: Expr | number\n    strength: Expr | number\n    skyGain: number\n}): Expr","params":[{"name":"x","type":"Expr","optional":false},{"name":"y","type":"Expr","optional":false},{"name":"opts","type":"{\n    /** A `direction()` vector that rotates the studio. */\n    rotation: Expr\n    keyRadius: Expr | number\n    drift: Expr | number\n    strength: Expr | number\n    skyGain: number\n}","optional":false}]},"materials.grainNoise":{"id":"materials.grainNoise","name":"grainNoise","qualifiedName":"grainNoise","category":"materials","module":"std/paint/materials.ts","summary":"One octave of smooth signed noise along and across a direction, for grain.","description":"`coords` come from `directionFrame(...).coordsOf(p)`. `freq` sets the feature density per axis (a high across value gives fine streaks). Returns roughly −1 to 1. Sum octaves and apply gains at the call site.","examples":["const sheen = grainNoise(brush.coordsOf(pat), {freq: [1.3, 6.5], drift: driftA})"],"tips":[],"see":["materials.surfaceNoise","materials.tiltNormal","materials.smearAlong"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.grainNoise"},"usedBy":["BrushedMetal","Satin"],"documented":true,"source":{"file":"std/paint/materials.ts","line":524},"kind":"function","signature":"grainNoise(coords: {along: Expr; across: Expr}, opts: {freq: [Expr | number, Expr | number]; offset?: [number, number]; drift?: Expr}): Expr","params":[{"name":"coords","type":"{along: Expr; across: Expr}","optional":false},{"name":"opts","type":"{freq: [Expr | number, Expr | number]; offset?: [number, number]; drift?: Expr}","optional":false}]},"materials.smearAlong":{"id":"materials.smearAlong","name":"smearAlong","qualifiedName":"smearAlong","category":"materials","module":"std/paint/materials.ts","summary":"Blurs any 2D lookup along a direction, for streaked reflections and motion smear.","description":"Samples `taps` points (default 9) spread `spread` either side of `center` along the unit vector `axis`, weighted by a bell curve, and sums them. `sample` receives an x and a y.","examples":["const env = smearAlong({center: R, axis: brush.tangent, spread: 0.3}, (x, y) => studioSoftboxes(x, y, studio))"],"tips":[],"see":["materials.studioSoftboxes","materials.grainNoise"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.smearAlong"},"usedBy":["BrushedMetal"],"documented":true,"source":{"file":"std/paint/materials.ts","line":548},"kind":"function","signature":"smearAlong(opts: {center: Expr; axis: Expr; spread: Expr; taps?: number; sigma?: number; hint?: string}, sample: (x: Expr, y: Expr) => Expr): Expr","params":[{"name":"opts","type":"{center: Expr; axis: Expr; spread: Expr; taps?: number; sigma?: number; hint?: string}","optional":false},{"name":"sample","type":"(x: Expr, y: Expr) => Expr","optional":false}]},"materials.lightVec3":{"id":"materials.lightVec3","name":"lightVec3","qualifiedName":"lightVec3","category":"materials","module":"std/paint/materials.ts","summary":"A 3D light direction from a `direction()` vector and an elevation.","description":"`z` is negative for a light on the viewer's side of the surface (−0.6 raking to −0.9 frontal). Use it where a word wants a unit light vector. `keyLightAt` also gives the half vector for highlights.","examples":["const L = lightVec3(direction(u.lightAngle), -0.7)"],"tips":[],"see":["materials.keyLightAt","materials.anisoSpecular","materials.lambert"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.lightVec3"},"usedBy":["BrushedMetal","CarbonFiber","Satin"],"documented":true,"source":{"file":"std/paint/materials.ts","line":588},"kind":"function","signature":"lightVec3(dir: Expr, z: number, hint: string = 'L'): Expr","params":[{"name":"dir","type":"Expr","optional":false},{"name":"z","type":"number","optional":false},{"name":"hint","type":"string","optional":true,"defaultValue":"'L'"}]},"materials.wardAlphas":{"id":"materials.wardAlphas","name":"wardAlphas","qualifiedName":"wardAlphas","category":"materials","module":"std/paint/materials.ts","summary":"Turns roughness and anisotropy sliders (0–1 each) into the two lobe widths `anisoSpecular` takes.","description":"Higher roughness widens the highlight. Higher anisotropy stretches it along the grain and squeezes it across.","examples":["alphas: wardAlphas(u.roughness, u.anisotropy)"],"tips":[],"see":["materials.anisoSpecular"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.wardAlphas"},"usedBy":["BrushedMetal","Satin"],"documented":true,"source":{"file":"std/paint/materials.ts","line":604},"kind":"function","signature":"wardAlphas(roughness: Expr | number, anisotropy: Expr | number): {along: Expr; across: Expr}","params":[{"name":"roughness","type":"Expr | number","optional":false},{"name":"anisotropy","type":"Expr | number","optional":false}]},"materials.anisoSpecular":{"id":"materials.anisoSpecular","name":"anisoSpecular","qualifiedName":"anisoSpecular","category":"materials","module":"std/paint/materials.ts","summary":"A highlight stretched along a grain, as on brushed metal or carbon fibre.","description":"`tangent` is the 2D grain direction, `light` a unit 3D light vector, `view` the view ray and `alphas` the widths from `wardAlphas`. Pass `gain` or scale the result yourself.","examples":["const spec = anisoSpecular({normal: n, tangent: brush.tangent, light: lightVec3(direction(u.lightAngle), -0.7), view, alphas: wardAlphas(rough, aniso), gain: u.specular})"],"tips":[],"see":["materials.wardAlphas","materials.lightVec3","materials.sharpGlint","materials.dualLobeGlint"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.anisoSpecular"},"usedBy":["BrushedMetal","CarbonFiber","Satin"],"documented":true,"source":{"file":"std/paint/materials.ts","line":626},"kind":"function","signature":"anisoSpecular(opts: {\n    normal: Expr\n    /** The 2D grain direction. */\n    tangent: Expr\n    /** A unit 3D light direction (see `lightVec3`). */\n    light: Expr\n    view: Expr\n    alphas: {along: Expr; across: Expr}\n    gain?: Expr | number\n}): Expr","params":[{"name":"opts","type":"{\n    normal: Expr\n    /** The 2D grain direction. */\n    tangent: Expr\n    /** A unit 3D light direction (see `lightVec3`). */\n    light: Expr\n    view: Expr\n    alphas: {along: Expr; across: Expr}\n    gain?: Expr | number\n}","optional":false}]},"materials.tiltAlong":{"id":"materials.tiltAlong","name":"tiltAlong","qualifiedName":"tiltAlong","category":"materials","module":"std/paint/materials.ts","summary":"Tilts a normal along one 2D direction by a slope amount.","description":"`axis` is a unit 2D vector such as `direction()` or `fieldSlope`. A few tenths is a visible tilt.","examples":["const n = tiltAlong(nGeo, weaveDir, mul(crown, 0.3))"],"tips":[],"see":["materials.tiltNormal","materials.nudgeNormal"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.tiltAlong"},"usedBy":["CarbonFiber"],"documented":true,"source":{"file":"std/paint/materials.ts","line":662},"kind":"function","signature":"tiltAlong(n: Expr, axis: Expr, amount: Expr, hint: string = 'n'): Expr","params":[{"name":"n","type":"Expr","optional":false},{"name":"axis","type":"Expr","optional":false},{"name":"amount","type":"Expr","optional":false},{"name":"hint","type":"string","optional":true,"defaultValue":"'n'"}]},"materials.fresnelBoost":{"id":"materials.fresnelBoost","name":"fresnelBoost","qualifiedName":"fresnelBoost","category":"materials","module":"std/paint/materials.ts","summary":"A multiplier that brightens grazing edges: 1 face-on, up to `1 + amount` at the rim.","description":"`grazing` comes from `grazingOf` or `grazingFlat`. `power` (default 3) sets how tightly the lift hugs the rim.","examples":["const lit = mul(env, fresnelBoost(grazingOf(n, view), {amount: 0.6}))"],"tips":[],"see":["materials.schlickFresnel","materials.grazingOf","materials.grazingFlat"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.fresnelBoost"},"usedBy":["BrushedMetal"],"documented":true,"source":{"file":"std/paint/materials.ts","line":682},"kind":"function","signature":"fresnelBoost(grazing: Expr, opts: {power?: number; amount: Expr | number}): Expr","params":[{"name":"grazing","type":"Expr","optional":false},{"name":"opts","type":"{power?: number; amount: Expr | number}","optional":false}]},"materials.tintRamp":{"id":"materials.tintRamp","name":"tintRamp","qualifiedName":"tintRamp","category":"materials","module":"std/paint/materials.ts","summary":"Maps a lighting value (0 dark, 1 light) onto two colors.","description":"Takes color props and returns rgb. The final tint of a material lit in grey.","examples":["const rgb = tintRamp(lit, u.darkColor, u.lightColor)"],"tips":[],"see":["materials.neutralTone","materials.cosineRainbow"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.tintRamp"},"usedBy":["BrushedMetal"],"documented":true,"source":{"file":"std/paint/materials.ts","line":698},"kind":"function","signature":"tintRamp(t: Expr, dark: Expr, light: Expr): Expr","params":[{"name":"t","type":"Expr","optional":false},{"name":"dark","type":"Expr","optional":false},{"name":"light","type":"Expr","optional":false}]},"materials.silhouette":{"id":"materials.silhouette","name":"silhouette","qualifiedName":"silhouette","category":"materials","module":"std/paint/materials.ts","summary":"The shape's coverage at this pixel: 1 inside, 0 outside, soft across the edge.","description":"`edgeSoftness` is the softness prop; at 0 the edge is still anti-aliased over 1.5 pixels. Use it as the alpha of the color you return.","examples":["return guarded(insideShape(field.sdf, field.pxH), vec4(rgb, silhouette(field, u.edgeSoftness)), vec4(0, 0, 0, 0))"],"tips":[],"see":["materials.insideShape","materials.guarded"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.silhouette"},"usedBy":["BrushedMetal","CarbonFiber","Chrome","Frost","Holographic","Irradiance","LiquidMetal","Nebula","Obsidian","Plastic","Water"],"documented":true,"source":{"file":"std/paint/materials.ts","line":714},"kind":"function","signature":"silhouette(field: SurfaceField, edgeSoftness: Expr): Expr","params":[{"name":"field","type":"SurfaceField","optional":false},{"name":"edgeSoftness","type":"Expr","optional":false}]},"materials.neutralTone":{"id":"materials.neutralTone","name":"neutralTone","qualifiedName":"neutralTone","category":"materials","module":"std/paint/materials.ts","summary":"Compresses bright color back into range without shifting its hue.","description":"The last step of a metal or glass look that adds reflection on top of a base color. `exposureTone` is the cousin for emitted light.","examples":["const rgb = neutralTone(mul(env, u.tint.member('rgb')))"],"tips":[],"see":["materials.exposureTone"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.neutralTone"},"usedBy":["Chrome","Nebula","Satin"],"documented":true,"source":{"file":"std/paint/materials.ts","line":730},"kind":"function","signature":"neutralTone(rgb: Expr): Expr","params":[{"name":"rgb","type":"Expr","optional":false}]},"materials.nudgeNormal":{"id":"materials.nudgeNormal","name":"nudgeNormal","qualifiedName":"nudgeNormal","category":"materials","module":"std/paint/materials.ts","summary":"Tilts a normal by a slope in x and y, turning a height field into relief.","description":"Feed it the `dx`/`dy` of `fdSlope` or `perlinSlope`, scaled by how strong you want the relief. Works on a flat `vec3(0, 0, -1)` normal as well as a shape's normal.","examples":["const n = nudgeNormal(vec3(0, 0, -1), mul(relief.dx, 0.08), mul(relief.dy, 0.08))"],"tips":[],"see":["materials.fdSlope","materials.perlinSlope","materials.tiltNormal","materials.geometricNormal"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.nudgeNormal"},"usedBy":["Chrome","Frost","LiquidMetal","Plastic","Satin"],"documented":true,"source":{"file":"std/paint/materials.ts","line":747},"kind":"function","signature":"nudgeNormal(n: Expr, dx: Expr, dy: Expr, hint: string = 'n'): Expr","params":[{"name":"n","type":"Expr","optional":false},{"name":"dx","type":"Expr","optional":false},{"name":"dy","type":"Expr","optional":false},{"name":"hint","type":"string","optional":true,"defaultValue":"'n'"}]},"materials.marchedNormal":{"id":"materials.marchedNormal","name":"marchedNormal","qualifiedName":"marchedNormal","category":"materials","module":"std/paint/materials.ts","summary":"The surface normal of a 3D shape, from the marched depth.","description":"Only meaningful when `frame.volumetric` is true. `geometricNormal` chooses between this and the flat-shape normal for you. `sharpness` clamps the slope (default 5; 4 is calmer, 6 crisper).","examples":["const n = frame.volumetric ? marchedNormal(field, 4) : flatNormal"],"tips":[],"see":["materials.geometricNormal"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.marchedNormal"},"usedBy":["Chrome","Crystal","Plastic","Water"],"documented":true,"source":{"file":"std/paint/materials.ts","line":766},"kind":"function","signature":"marchedNormal(field: SurfaceField, sharpness: number = 5, hint: string = 'nGeo'): Expr","params":[{"name":"field","type":"SurfaceField","optional":false},{"name":"sharpness","type":"number","optional":true,"defaultValue":"5"},{"name":"hint","type":"string","optional":true,"defaultValue":"'nGeo'"}]},"materials.keyLightAt":{"id":"materials.keyLightAt","name":"keyLightAt","qualifiedName":"keyLightAt","category":"materials","module":"std/paint/materials.ts","summary":"A key light from an angle: the light direction `.L` and the half vector `.H` for highlights.","description":"`dir` is a `direction()` vector (from `frames`). `elevation` is how far the light sits toward the viewer: −0.6 raking, −0.9 frontal. Read `.L` with `member('L')` for `lambert` and `.H` for a glint.","examples":["const key = keyLightAt(direction(u.lightAngle), -0.7)"],"tips":["Elevation is negative in this engine. A positive value lights the surface from behind."],"see":["materials.keyLightXY","materials.lightVec3","materials.lambert","materials.dualLobeGlint","materials.sharpGlint"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.keyLightAt"},"usedBy":["Frost","Goo","LiquidMetal","Nebula","Plastic","Water"],"documented":true,"source":{"file":"std/paint/materials.ts","line":784},"kind":"function","signature":"keyLightAt(dir: Expr, elevation: number, hint: string = 'kl'): Expr","params":[{"name":"dir","type":"Expr","optional":false},{"name":"elevation","type":"number","optional":false},{"name":"hint","type":"string","optional":true,"defaultValue":"'kl'"}]},"materials.keyLightXY":{"id":"materials.keyLightXY","name":"keyLightXY","qualifiedName":"keyLightXY","category":"materials","module":"std/paint/materials.ts","summary":"A key light from explicit x and y components, for a second or a crossed light.","description":"Same result as `keyLightAt`: `.L` and `.H`.","examples":["const fill = keyLightXY(mul(ly, -1), lx, -0.4, 'fill')"],"tips":[],"see":["materials.keyLightAt"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.keyLightXY"},"usedBy":["Crystal"],"documented":true,"source":{"file":"std/paint/materials.ts","line":800},"kind":"function","signature":"keyLightXY(x: Expr, y: Expr, elevation: number, hint: string = 'kl'): Expr","params":[{"name":"x","type":"Expr","optional":false},{"name":"y","type":"Expr","optional":false},{"name":"elevation","type":"number","optional":false},{"name":"hint","type":"string","optional":true,"defaultValue":"'kl'"}]},"materials.grazingFlat":{"id":"materials.grazingFlat","name":"grazingFlat","qualifiedName":"grazingFlat","category":"materials","module":"std/paint/materials.ts","summary":"How edge-on the surface is to a straight-on viewer: 0 face-on, 1 grazing.","description":"Use it when the material has no `viewRay`. The fresnel input for plastics, frost and water.","examples":["const grazing = grazingFlat(n)"],"tips":[],"see":["materials.grazingOf","materials.schlickFresnel","materials.fresnelBoost"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.grazingFlat"},"usedBy":["Crystal","Frost","Goo","Plastic","Water"],"documented":true,"source":{"file":"std/paint/materials.ts","line":815},"kind":"function","signature":"grazingFlat(n: Expr): Expr","params":[{"name":"n","type":"Expr","optional":false}]},"materials.sharpGlint":{"id":"materials.sharpGlint","name":"sharpGlint","qualifiedName":"sharpGlint","category":"materials","module":"std/paint/materials.ts","summary":"A highlight whose tightness follows a sharpness slider.","description":"`ndh` is the dot of the normal and the key light's `.H`. `sharpness` runs 0–1 and slides the exponent from `cfg.x` to `cfg.y` and the gain from `cfg.z` to `cfg.w`. Pass `cfg` as `vec4(soft exponent, sharp exponent, soft gain, sharp gain)`.","examples":["const spec = sharpGlint(dot(n, key.member('H')), u.sharpness, vec4(60, 900, 0.4, 1.4))"],"tips":[],"see":["materials.dualLobeGlint","materials.anisoSpecular","materials.keyLightAt"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.sharpGlint"},"usedBy":["LiquidMetal","Water"],"documented":true,"source":{"file":"std/paint/materials.ts","line":832},"kind":"function","signature":"sharpGlint(ndh: Expr, sharpness: Expr, cfg: Expr): Expr","params":[{"name":"ndh","type":"Expr","optional":false},{"name":"sharpness","type":"Expr","optional":false},{"name":"cfg","type":"Expr","optional":false}]},"materials.perlinSlope":{"id":"materials.perlinSlope","name":"perlinSlope","qualifiedName":"perlinSlope","category":"materials","module":"std/paint/materials.ts","summary":"The slope of smooth noise at a point, for wavy or molten relief.","description":"Returns a 2D slope scaled by `gain` and clamped to ±4, so relief never spikes into seams. Feed its x and y to `nudgeNormal`.","examples":["const hg = perlinSlope(pat, 0.62)\nconst n = nudgeNormal(nGeo, mul(hg.member('x'), -0.5), mul(hg.member('y'), -0.5))"],"tips":[],"see":["materials.nudgeNormal","materials.fdSlope","materials.flowWarp"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.perlinSlope"},"usedBy":["Chrome","LiquidMetal"],"documented":true,"source":{"file":"std/paint/materials.ts","line":850},"kind":"function","signature":"perlinSlope(q: Expr, gain: Expr | number): Expr","params":[{"name":"q","type":"Expr","optional":false},{"name":"gain","type":"Expr | number","optional":false}]},"materials.opticalThickness":{"id":"materials.opticalThickness","name":"opticalThickness","qualifiedName":"opticalThickness","category":"materials","module":"std/paint/materials.ts","summary":"How much material the view passes through at this pixel: 0 at the edge, up to about 1.2 deep inside.","description":"Multiply by a density prop and feed `beerLambert` for glass, ice and water.","examples":["const thickness = mul(opticalThickness(field, frame), u.density)"],"tips":[],"see":["materials.beerLambert"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.opticalThickness"},"usedBy":["Frost","Water"],"documented":true,"source":{"file":"std/paint/materials.ts","line":866},"kind":"function","signature":"opticalThickness(field: SurfaceField, frame: SurfaceFrame): Expr","params":[{"name":"field","type":"SurfaceField","optional":false},{"name":"frame","type":"SurfaceFrame","optional":false}]},"materials.beerLambert":{"id":"materials.beerLambert","name":"beerLambert","qualifiedName":"beerLambert","category":"materials","module":"std/paint/materials.ts","summary":"How much light survives a thickness of tinted material, per channel.","description":"`absorb` is an rgb absorption; higher absorbs more. Returns rgb transmission 0–1. Multiply the color behind by it.","examples":["const transmit = beerLambert(mul(opticalThickness(field, frame), u.density), absorb)"],"tips":[],"see":["materials.opticalThickness"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.beerLambert"},"usedBy":["Frost","Water"],"documented":true,"source":{"file":"std/paint/materials.ts","line":883},"kind":"function","signature":"beerLambert(thickness: Expr, absorb: Expr): Expr","params":[{"name":"thickness","type":"Expr","optional":false},{"name":"absorb","type":"Expr","optional":false}]},"materials.flowWarp":{"id":"materials.flowWarp","name":"flowWarp","qualifiedName":"flowWarp","category":"materials","module":"std/paint/materials.ts","summary":"A slowly folding 2D offset to add to pattern coordinates, for molten or wind-blown motion.","description":"`warpInput` is the pattern coordinate scaled by about 0.6, `flowT` the layer's clock times a speed. Add the result to the coordinate before sampling noise.","examples":["const q = add(pat, mul(flowWarp(mul(pat, 0.6), flowT), u.warp))"],"tips":[],"see":["materials.surfaceNoiseAt","materials.perlinSlope"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.flowWarp"},"usedBy":["LiquidMetal","Water"],"documented":true,"source":{"file":"std/paint/materials.ts","line":900},"kind":"function","signature":"flowWarp(warpInput: Expr, flowT: Expr): Expr","params":[{"name":"warpInput","type":"Expr","optional":false},{"name":"flowT","type":"Expr","optional":false}]},"materials.surfaceNoise":{"id":"materials.surfaceNoise","name":"surfaceNoise","qualifiedName":"surfaceNoise","category":"materials","module":"std/paint/materials.ts","summary":"Smooth signed noise at a 2D point, roughly −1 to 1, with features about one unit wide.","description":"The basis for grain, crumple and frost. Scale the inputs to set the feature size.","examples":["const grain = surfaceNoise(mul(pat.member('x'), 140), mul(pat.member('y'), 140))"],"tips":[],"see":["materials.surfaceNoiseAt","materials.valueNoise","materials.hashNoise","materials.grainNoise"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.surfaceNoise"},"usedBy":["CarbonFiber","Frost","Hologram","Holographic","Plastic"],"documented":true,"source":{"file":"std/paint/materials.ts","line":916},"kind":"function","signature":"surfaceNoise(x: Expr | number, y: Expr | number): Expr","params":[{"name":"x","type":"Expr | number","optional":false},{"name":"y","type":"Expr | number","optional":false}]},"materials.surfaceNoiseAt":{"id":"materials.surfaceNoiseAt","name":"surfaceNoiseAt","qualifiedName":"surfaceNoiseAt","category":"materials","module":"std/paint/materials.ts","summary":"`surfaceNoise` at a 2D point you already hold.","description":"","examples":["const n = surfaceNoiseAt(mul(pat, 6))"],"tips":[],"see":["materials.surfaceNoise","materials.volumeNoiseAt","materials.cellNoiseAt"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.surfaceNoiseAt"},"usedBy":["Marble","Nebula","Plastic","Water"],"documented":true,"source":{"file":"std/paint/materials.ts","line":929},"kind":"function","signature":"surfaceNoiseAt(p: Expr): Expr","params":[{"name":"p","type":"Expr","optional":false}]},"materials.volumeNoiseAt":{"id":"materials.volumeNoiseAt","name":"volumeNoiseAt","qualifiedName":"volumeNoiseAt","category":"materials","module":"std/paint/materials.ts","summary":"Smooth signed noise at a 3D point, roughly −1 to 1.","description":"For anything sampled through a body rather than across its surface: gas, smoke, inclusions, density.","examples":["const density = volumeNoiseAt(vec3(pos.member('x'), pos.member('y'), t))"],"tips":[],"see":["materials.surfaceNoiseAt"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.volumeNoiseAt"},"usedBy":["Aurora","FlowingGradient","MeshGradient"],"documented":true,"source":{"file":"std/paint/materials.ts","line":945},"kind":"function","signature":"volumeNoiseAt(p: Expr): Expr","params":[{"name":"p","type":"Expr","optional":false}]},"materials.cosineRainbow":{"id":"materials.cosineRainbow","name":"cosineRainbow","qualifiedName":"cosineRainbow","category":"materials","module":"std/paint/materials.ts","summary":"A smooth rainbow color from a value that wraps every 1.","description":"`phases` offset the red, green and blue cycles; `[0, 1/3, 2/3]` is the full spectrum. The color behind holographic foil and thin-film iridescence.","examples":["const rgb = cosineRainbow(mul(grazing, 3), [0, 1 / 3, 2 / 3])"],"tips":[],"see":["materials.tintRamp"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.cosineRainbow"},"usedBy":["Holographic"],"documented":true,"source":{"file":"std/paint/materials.ts","line":961},"kind":"function","signature":"cosineRainbow(t: Expr, phases: [number, number, number]): Expr","params":[{"name":"t","type":"Expr","optional":false},{"name":"phases","type":"[number, number, number]","optional":false}]},"materials.cellNoiseAt":{"id":"materials.cellNoiseAt","name":"cellNoiseAt","qualifiedName":"cellNoiseAt","category":"materials","module":"std/paint/materials.ts","summary":"Cellular noise at a 2D point: 0 at each cell's seed point, rising toward the cell walls.","description":"The value is a squared distance in cell units. Use it for facets and cracks, or as the field `fdSlope` differentiates for faceted relief.","examples":["const facet = fdSlope(cellNoiseAt, mul(pat, 6), 0.002)"],"tips":[],"see":["materials.fdSlope","materials.surfaceNoiseAt","materials.pointStars"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.cellNoiseAt"},"usedBy":["Crystal"],"documented":true,"source":{"file":"std/paint/materials.ts","line":978},"kind":"function","signature":"cellNoiseAt(p: Expr): Expr","params":[{"name":"p","type":"Expr","optional":false}]},"materials.fdSlope":{"id":"materials.fdSlope","name":"fdSlope","qualifiedName":"fdSlope","category":"materials","module":"std/paint/materials.ts","summary":"The value and slope of any field at a point: `value`, `dx` and `dy`.","description":"`sampleAt` is the field as a function of a 2D point, `eps` the tap distance in that point's units (a few thousandths in uv). The bridge from a height field to a normal: feed `dx` and `dy` to `nudgeNormal`.","examples":["const relief = fdSlope((at) => heightField(params, at), uv, 0.0035)"],"tips":["Three taps of the field per pixel. Keep the field cheap or `share` it."],"see":["materials.nudgeNormal","materials.perlinSlope","materials.cellNoiseAt"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.fdSlope"},"usedBy":["Crystal","Satin"],"documented":true,"source":{"file":"std/paint/materials.ts","line":997},"kind":"function","signature":"fdSlope(sampleAt: (p: Expr) => Expr, p: Expr, eps: number, hint: string = 'fd'): {value: Expr; dx: Expr; dy: Expr}","params":[{"name":"sampleAt","type":"(p: Expr) => Expr","optional":false},{"name":"p","type":"Expr","optional":false},{"name":"eps","type":"number","optional":false},{"name":"hint","type":"string","optional":true,"defaultValue":"'fd'"}]},"materials.fdCurvature":{"id":"materials.fdCurvature","name":"fdCurvature","qualifiedName":"fdCurvature","category":"materials","module":"std/paint/materials.ts","summary":"The value, slope and curvature of any field at a point: `value`, `dx`, `dy` and `curvature`.","description":"`fdSlope` with two more taps. The slopes are centred, so they sit on the point rather than half a step past it, and `curvature` is the field's Laplacian: negative on a crest, positive in a crease, zero on a flat or a plane. Scale it by a gain for a cavity shade that darkens creases and lifts crests. `eps` is the tap distance in the point's units.","examples":["const relief = fdCurvature(cloth, q, 0.004)\nconst cavity = clamp(sub(1, mul(relief.curvature, 0.005)), 0.6, 1.1)"],"tips":["Five taps of the field per pixel. Keep the field cheap."],"see":["materials.fdSlope","materials.nudgeNormal"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.fdCurvature"},"usedBy":["Satin"],"documented":true,"source":{"file":"std/paint/materials.ts","line":1027},"kind":"function","signature":"fdCurvature(sampleAt: (p: Expr) => Expr, p: Expr, eps: number, hint: string = 'fd'): {value: Expr; dx: Expr; dy: Expr; curvature: Expr}","params":[{"name":"sampleAt","type":"(p: Expr) => Expr","optional":false},{"name":"p","type":"Expr","optional":false},{"name":"eps","type":"number","optional":false},{"name":"hint","type":"string","optional":true,"defaultValue":"'fd'"}]},"materials.hashNoise":{"id":"materials.hashNoise","name":"hashNoise","qualifiedName":"hashNoise","category":"materials","module":"std/paint/materials.ts","summary":"Random 0–1 per point, with no smoothness between neighbours.","description":"For grain, flicker and per-pixel jitter. Feed it large coordinates so every pixel differs.","examples":["const grain = hashNoise(mul(uv.member('x'), 991.3), mul(uv.member('y'), 787.7))"],"tips":[],"see":["materials.interleavedNoise","materials.valueNoise","materials.sensorGrain"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.hashNoise"},"usedBy":["Heatmap","Hologram","Nebula"],"documented":true,"source":{"file":"std/paint/materials.ts","line":1057},"kind":"function","signature":"hashNoise(x: Expr | number, y: Expr | number): Expr","params":[{"name":"x","type":"Expr | number","optional":false},{"name":"y","type":"Expr | number","optional":false}]},"materials.interleavedNoise":{"id":"materials.interleavedNoise","name":"interleavedNoise","qualifiedName":"interleavedNoise","category":"materials","module":"std/paint/materials.ts","summary":"A 0–1 dither pattern over device pixels where neighbours differ as much as possible.","description":"Better than `hashNoise` for jittering sample positions, which would otherwise clump into visible grain. `pixel` is the pixel coordinate (uv times the viewport size). `clock` shifts the pattern each frame.","examples":["const jitter = interleavedNoise(mul(uv, viewport), frame)"],"tips":[],"see":["materials.hashNoise","materials.sensorGrain"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.interleavedNoise"},"usedBy":[],"documented":true,"source":{"file":"std/paint/materials.ts","line":1074},"kind":"function","signature":"interleavedNoise(pixel: Expr, clock: Expr | number = 0): Expr","params":[{"name":"pixel","type":"Expr","optional":false},{"name":"clock","type":"Expr | number","optional":true,"defaultValue":"0"}]},"materials.valueNoise":{"id":"materials.valueNoise","name":"valueNoise","qualifiedName":"valueNoise","category":"materials","module":"std/paint/materials.ts","summary":"Smooth noise 0–1 at a 2D point, softer and blockier than `surfaceNoise`.","description":"","examples":["const wobble = sub(valueNoise(mul(x, 4), mul(y, 3)), 0.5)"],"tips":[],"see":["materials.surfaceNoise","materials.hashNoise"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.valueNoise"},"usedBy":["Heatmap"],"documented":true,"source":{"file":"std/paint/materials.ts","line":1090},"kind":"function","signature":"valueNoise(x: Expr | number, y: Expr | number): Expr","params":[{"name":"x","type":"Expr | number","optional":false},{"name":"y","type":"Expr | number","optional":false}]},"materials.rotationSensor":{"id":"materials.rotationSensor","name":"rotationSensor","qualifiedName":"rotationSensor","category":"materials","module":"std/paint/materials.ts","summary":"How far a 3D shape has rotated, as a 2D pan you can move content by.","description":"Zero on flat shapes and on any shape at rest, growing as the shape turns. Constant across the frame, so it pans a star field or a backdrop without shearing it. Needs `pattern: 'raw'`.","examples":["const pan = rotationSensor(frame)"],"tips":[],"see":["materials.shellRefract","materials.surfacePattern"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.rotationSensor"},"usedBy":["Nebula"],"documented":true,"source":{"file":"std/paint/materials.ts","line":1106},"kind":"function","signature":"rotationSensor(frame: SurfaceFrame): {x: Expr; y: Expr}","params":[{"name":"frame","type":"SurfaceFrame","optional":false}]},"materials.shellRefract":{"id":"materials.shellRefract","name":"shellRefract","qualifiedName":"shellRefract","category":"materials","module":"std/paint/materials.ts","summary":"Coordinates for what sits inside a glass shell, bent where the shell curves.","description":"Returns `x` and `y` centred on the shape and displaced by the normal's tilt times `amount`, plus the unbent `centered` pair for anything fixed to the container. The bend is strongest at bevels and silhouettes, where real glass bends its interior.","examples":["const interior = shellRefract(frame, n, u.bend)"],"tips":[],"see":["materials.rotationSensor","math.refract"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.shellRefract"},"usedBy":["Nebula"],"documented":true,"source":{"file":"std/paint/materials.ts","line":1129},"kind":"function","signature":"shellRefract(frame: SurfaceFrame, n: Expr, amount: Expr): {\n    x: Expr\n    y: Expr\n    centered: {x: Expr; y: Expr}\n}","params":[{"name":"frame","type":"SurfaceFrame","optional":false},{"name":"n","type":"Expr","optional":false},{"name":"amount","type":"Expr","optional":false}]},"materials.schlickFresnel":{"id":"materials.schlickFresnel","name":"schlickFresnel","qualifiedName":"schlickFresnel","category":"materials","module":"std/paint/materials.ts","summary":"How mirror-like a surface is at this pixel: `r0` face-on, rising to `r0 + gain` at the rim.","description":"`grazing` comes from `grazingOf` or `grazingFlat`. Glass is `{r0: 0.04, gain: 0.96}`. Lower the gain to keep the rim from turning into a full mirror over an interior.","examples":["const fresnel = schlickFresnel(grazingOf(n, view), {gain: 0.6})"],"tips":[],"see":["materials.fresnelBoost","materials.grazingOf","math.reflect"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.schlickFresnel"},"usedBy":["Obsidian","Voxels","Water"],"documented":true,"source":{"file":"std/paint/materials.ts","line":1157},"kind":"function","signature":"schlickFresnel(grazing: Expr, opts: {r0?: number; gain: Expr | number; power?: number}): Expr","params":[{"name":"grazing","type":"Expr","optional":false},{"name":"opts","type":"{r0?: number; gain: Expr | number; power?: number}","optional":false}]},"materials.twinkle":{"id":"materials.twinkle","name":"twinkle","qualifiedName":"twinkle","category":"materials","module":"std/paint/materials.ts","summary":"A brightness factor that shimmers over time, different for each element.","description":"Ranges from `1 − 2·amount` to 1. `phase` is a per-element value (a noise or a hash) that keeps the elements out of step; `rate` is how fast, `spread` how differently they flicker.","examples":["shimmer: (v) => twinkle(t, v, {amount: 0.3, rate: 1.7, spread: 40})"],"tips":[],"see":["materials.pointStars"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.twinkle"},"usedBy":["Nebula"],"documented":true,"source":{"file":"std/paint/materials.ts","line":1174},"kind":"function","signature":"twinkle(t: Expr, phase: Expr, opts: {amount: Expr; rate: number; spread: number}): Expr","params":[{"name":"t","type":"Expr","optional":false},{"name":"phase","type":"Expr","optional":false},{"name":"opts","type":"{amount: Expr; rate: number; spread: number}","optional":false}]},"materials.pointStars":{"id":"materials.pointStars","name":"pointStars","qualifiedName":"pointStars","category":"materials","module":"std/paint/materials.ts","summary":"A plane of sparse glinting points: stars, glitter, dust.","description":"`p` is the plane coordinate; scale it to set density. Returns the glint intensity, the slow per-point `variation` field, and `rgb` when `tint` is given. Radii are in the cellular noise's squared-distance units, so they are small (0.004–0.02).","examples":["const stars = pointStars(mul(pat, 40), {variationFreq: 0.13, radius: [0.004, 0.013], keep: [0.35, 0.75], brightness: [0.3, 1.2], gain: u.stars, tint: {from: [0.72, 0.82, 1], to: [1, 0.92, 0.82], gain: 1.6}})"],"tips":[],"see":["materials.twinkle","materials.cellNoiseAt"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.pointStars"},"usedBy":["Nebula"],"documented":true,"source":{"file":"std/paint/materials.ts","line":1192},"kind":"function","signature":"pointStars(p: Expr, opts: {\n    /** Frequency of the per-point variation field relative to `p` (slow, about 0.1). */\n    variationFreq: number\n    /** Point radius as `[base, jitter]`: base plus variation times jitter. */\n    radius: [number, number]\n    /** The band of the variation field that keeps a point at all. Narrower is sparser. */\n    keep: [number, number]\n    /** Brightness as `[base, gain]`: base plus variation squared times gain. */\n    brightness: [number, number]\n    /** A per-point shimmer factor built from the variation field (see `twinkle`). */\n    shimmer?: (variation: Expr) => Expr\n    /** Overall gain. */\n    gain: Expr\n    /** Color the glints: variation mixes `from` to `to`, scaled by the glint times `gain`. */\n    tint?: {from: [number, number, number]; to: [number, number, number]; gain: number}\n    hint?: string\n}): {glint: Expr; variation: Expr; rgb?: Expr}","params":[{"name":"p","type":"Expr","optional":false},{"name":"opts","type":"{\n    /** Frequency of the per-point variation field relative to `p` (slow, about 0.1). */\n    variationFreq: number\n    /** Point radius as `[base, jitter]`: base plus variation times jitter. */\n    radius: [number, number]\n    /** The band of the variation field that keeps a point at all. Narrower is sparser. */\n    keep: [number, number]\n    /** Brightness as `[base, gain]`: base plus variation squared times gain. */\n    brightness: [number, number]\n    /** A per-point shimmer factor built from the variation field (see `twinkle`). */\n    shimmer?: (variation: Expr) => Expr\n    /** Overall gain. */\n    gain: Expr\n    /** Color the glints: variation mixes `from` to `to`, scaled by the glint times `gain`. */\n    tint?: {from: [number, number, number]; to: [number, number, number]; gain: number}\n    hint?: string\n}","optional":false}]},"materials.placementPoint":{"id":"materials.placementPoint","name":"placementPoint","qualifiedName":"placementPoint","category":"materials","module":"std/paint/materials.ts","summary":"A position prop in the shape's own coordinates, so you can compare it with the field.","description":"Takes a `transformPosition` prop and applies the same center, scale and rotation the shape has. Distances from it are in placement units, not canvas units.","examples":["const lightUV = placementPoint(params, params.uniforms.lightPosition)"],"tips":[],"see":["materials.pointLightFrom","materials.nearestEdge"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.placementPoint"},"usedBy":["Irradiance"],"documented":true,"source":{"file":"std/paint/materials.ts","line":1247},"kind":"function","signature":"placementPoint(params: GpuFragmentParams, position: Expr, hint: string = 'placed'): Expr","params":[{"name":"params","type":"GpuFragmentParams","optional":false},{"name":"position","type":"Expr","optional":false},{"name":"hint","type":"string","optional":true,"defaultValue":"'placed'"}]},"materials.continuedField":{"id":"materials.continuedField","name":"continuedField","qualifiedName":"continuedField","category":"materials","module":"std/paint/materials.ts","summary":"The distance field extended past the edge of an SVG shape's texture.","description":"An SVG field stops at its 0–1 square, so a far-reaching glow or shadow would see a plateau beyond it. Returns the extended distance here, an `outward` push to add to any slope taken near the border, and `at(uv)` for the distance anywhere. Pass an unscaled `surfaceField`. Flat and 3D shapes pass through unchanged.","examples":["const far = continuedField(frame, field, params)"],"tips":[],"see":["materials.nearestEdge","materials.fieldSlope"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.continuedField"},"usedBy":["Irradiance"],"documented":true,"source":{"file":"std/paint/materials.ts","line":1268},"kind":"function","signature":"continuedField(frame: SurfaceFrame, field: SurfaceField, params: GpuFragmentParams): {sdf: Expr; outward: Expr; at: (uv: Expr) => Expr}","params":[{"name":"frame","type":"SurfaceFrame","optional":false},{"name":"field","type":"SurfaceField","optional":false},{"name":"params","type":"GpuFragmentParams","optional":false}]},"materials.nearestEdge":{"id":"materials.nearestEdge","name":"nearestEdge","qualifiedName":"nearestEdge","category":"materials","module":"std/paint/materials.ts","summary":"The closest point on the shape's edge to this pixel, and the outward normal there.","description":"Where a rim glow or contact shadow comes from for pixels outside the shape. `slope` is `fieldSlope`; its length is guarded.","examples":["const edge = nearestEdge(frame.sdfUV!, field.sdf, fieldSlope(field))"],"tips":[],"see":["materials.fieldSlope","materials.pointLightFrom","materials.continuedField"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.nearestEdge"},"usedBy":["Irradiance"],"documented":true,"source":{"file":"std/paint/materials.ts","line":1305},"kind":"function","signature":"nearestEdge(sdfUV: Expr, sdf: Expr, slope: Expr, hint: string = 'edge'): {point: Expr; normal: Expr}","params":[{"name":"sdfUV","type":"Expr","optional":false},{"name":"sdf","type":"Expr","optional":false},{"name":"slope","type":"Expr","optional":false},{"name":"hint","type":"string","optional":true,"defaultValue":"'edge'"}]},"materials.pointLightFrom":{"id":"materials.pointLightFrom","name":"pointLightFrom","qualifiedName":"pointLightFrom","category":"materials","module":"std/paint/materials.ts","summary":"A point light as seen from a point on the shape: the 3D light vector `L`, the 2D direction `toLight` and the `distance`.","description":"`position` is in placement coordinates (see `placementPoint`). `height` lifts the light toward the viewer, in the same units. Feed `L` to `lambert` and `distance` to `inverseSquare`.","examples":["const onBody = pointLightFrom({position: lightUV, height: u.lightHeight}, frame.sdfUV!)"],"tips":[],"see":["materials.placementPoint","materials.lambert","materials.inverseSquare","materials.keyLightAt"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.pointLightFrom"},"usedBy":["Irradiance"],"documented":true,"source":{"file":"std/paint/materials.ts","line":1323},"kind":"function","signature":"pointLightFrom(light: {position: Expr; height: Expr | number}, at: Expr, hint: string = 'pl'): {L: Expr; toLight: Expr; distance: Expr}","params":[{"name":"light","type":"{position: Expr; height: Expr | number}","optional":false},{"name":"at","type":"Expr","optional":false},{"name":"hint","type":"string","optional":true,"defaultValue":"'pl'"}]},"materials.lambert":{"id":"materials.lambert","name":"lambert","qualifiedName":"lambert","category":"materials","module":"std/paint/materials.ts","summary":"How much a light falls on a surface: 0 facing away, 1 facing it.","description":"`normal` and `light` are unit vectors, both 2D or both 3D. `wrap` lets light reach around the shape: 0 is a hard terminator, 1 lights it all the way round.","examples":["const diffuse = lambert(n, key.member('L'), {wrap: 0.3})"],"tips":[],"see":["materials.keyLightAt","materials.pointLightFrom","materials.dualLobeGlint"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.lambert"},"usedBy":["Irradiance","Satin","Voxels"],"documented":true,"source":{"file":"std/paint/materials.ts","line":1348},"kind":"function","signature":"lambert(normal: Expr, light: Expr, opts: {wrap?: Expr | number} = {}): Expr","params":[{"name":"normal","type":"Expr","optional":false},{"name":"light","type":"Expr","optional":false},{"name":"opts","type":"{wrap?: Expr | number}","optional":true,"defaultValue":"{}"}]},"materials.inverseSquare":{"id":"materials.inverseSquare","name":"inverseSquare","qualifiedName":"inverseSquare","category":"materials","module":"std/paint/materials.ts","summary":"How a point light fades with distance: 1 at the source, half at `range`.","description":"","examples":["const lit = mul(lambert(n, onBody.L), inverseSquare(onBody.distance, u.lightRange))"],"tips":[],"see":["materials.pointLightFrom","materials.lambert"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.inverseSquare"},"usedBy":["Irradiance"],"documented":true,"source":{"file":"std/paint/materials.ts","line":1363},"kind":"function","signature":"inverseSquare(distance: Expr, range: Expr | number): Expr","params":[{"name":"distance","type":"Expr","optional":false},{"name":"range","type":"Expr | number","optional":false}]},"materials.exposureTone":{"id":"materials.exposureTone","name":"exposureTone","qualifiedName":"exposureTone","category":"materials","module":"std/paint/materials.ts","summary":"Turns light that piles past 1 into a soft burn toward white, like film.","description":"Apply it to summed emitted light before output. `neutralTone` is the cousin for a lit, tinted surface.","examples":["const glow = exposureTone(add(irradiance, emitter))"],"tips":[],"see":["materials.neutralTone","materials.sensorGrain"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.exposureTone"},"usedBy":["Irradiance"],"documented":true,"source":{"file":"std/paint/materials.ts","line":1380},"kind":"function","signature":"exposureTone(rgb: Expr): Expr","params":[{"name":"rgb","type":"Expr","optional":false}]},"materials.sensorGrain":{"id":"materials.sensorGrain","name":"sensorGrain","qualifiedName":"sensorGrain","category":"materials","module":"std/paint/materials.ts","summary":"Adds a fine per-pixel brightness jitter, so rendered light reads as photographed.","description":"Each pixel is scaled by up to half of `amount` either way. `pixel` is the pixel coordinate (uv times the viewport size); `clock` re-rolls the grain each frame.","examples":["const rgb = sensorGrain(glow, {amount: 0.08, pixel: mul(uv, viewport), clock: t})"],"tips":[],"see":["materials.hashNoise","materials.interleavedNoise","materials.exposureTone"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.sensorGrain"},"usedBy":[],"documented":true,"source":{"file":"std/paint/materials.ts","line":1397},"kind":"function","signature":"sensorGrain(rgb: Expr, opts: {amount: Expr | number; pixel: Expr; clock: Expr | number}): Expr","params":[{"name":"rgb","type":"Expr","optional":false},{"name":"opts","type":"{amount: Expr | number; pixel: Expr; clock: Expr | number}","optional":false}]},"materials.dualLobeGlint":{"id":"materials.dualLobeGlint","name":"dualLobeGlint","qualifiedName":"dualLobeGlint","category":"materials","module":"std/paint/materials.ts","summary":"A highlight with a tight hot core and a wide dim halo.","description":"`ndh` is the dot of the normal and the key light's `.H`. `core` and `halo` are `[exponent, gain]` pairs. `softness` (0–1, 0.5 neutral) widens or tightens both lobes.","examples":["const spec = dualLobeGlint(dot(n, key.member('H')), {core: [140, 1.3], halo: [22, 0.14], softness: u.highlightSoftness, gain: u.highlight})"],"tips":[],"see":["materials.sharpGlint","materials.keyLightAt","materials.lambert"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.dualLobeGlint"},"usedBy":["Nebula"],"documented":true,"source":{"file":"std/paint/materials.ts","line":1415},"kind":"function","signature":"dualLobeGlint(ndh: Expr, opts: {\n    /** `[exponent, gain]` of the hot core lobe. */\n    core: [number, number]\n    /** `[exponent, gain]` of the wide halo lobe. */\n    halo: [number, number]\n    softness?: Expr\n    gain: Expr\n}): Expr","params":[{"name":"ndh","type":"Expr","optional":false},{"name":"opts","type":"{\n    /** `[exponent, gain]` of the hot core lobe. */\n    core: [number, number]\n    /** `[exponent, gain]` of the wide halo lobe. */\n    halo: [number, number]\n    softness?: Expr\n    gain: Expr\n}","optional":false}]},"materials.sheenLobe":{"id":"materials.sheenLobe","name":"sheenLobe","qualifiedName":"sheenLobe","category":"materials","module":"std/paint/materials.ts","summary":"The soft glow of light scattered off fibres where a surface turns edge-on to the viewer: the velvet sheen on the flanks of satin, velour and wool.","description":"`normal` and `light` are unit 3D vectors and `view` the view ray (`viewRay`, or a constant `vec3(0, 0, 1)`). It is zero on a face-on surface and rises along each fold's flank, so it never washes a flat sheet. `roughness` (0–1) widens it: low is a thin bright rim, high a broad soft bloom. The lobe peaks at 1 and is held back by a wrapped lambert so unlit flanks stay dark; pass `gain` or scale the result yourself. Pair it with `anisoSpecular` for a satin finish: the streak is the shine, the sheen is the body.","examples":["const velvet = sheenLobe({normal: n, light: L, view, roughness: 0.5, gain: u.sheen})"],"tips":[],"see":["materials.anisoSpecular","materials.lambert","materials.grazingOf"],"import":{"statement":"import {materials} from 'shaders/std'","usage":"materials.sheenLobe"},"usedBy":["Satin"],"documented":true,"source":{"file":"std/paint/materials.ts","line":1449},"kind":"function","signature":"sheenLobe(opts: {\n    normal: Expr\n    light: Expr\n    view: Expr\n    roughness: Expr | number\n    gain?: Expr | number\n}): Expr","params":[{"name":"opts","type":"{\n    normal: Expr\n    light: Expr\n    view: Expr\n    roughness: Expr | number\n    gain?: Expr | number\n}","optional":false}]},"volume.interiorRay":{"id":"volume.interiorRay","name":"interiorRay","qualifiedName":"interiorRay","category":"volume","module":"std/paint/volume.ts","summary":"The line of sight through a shape: where it enters, how far it travels inside, and where it is at any depth.","description":"Pass the material's `field` and `frame`, the point the ray enters at (`origin`, usually the shape's centred placement coordinates) and optionally the `view` ray. `shear` (default 1) sets how far deeper samples slide sideways along the view, which is what makes an interior read as deep rather than flat. `lengthScale` stretches or shortens the travel. On a 3D shape `length` is the real chord through it. On a flat shape it stands in for depth, growing toward the middle of the shape.","examples":["const ray = interiorRay(field, frame, {origin: interior, view, shear: 0.55})"],"tips":["Refract `origin` through the shell first (`shellRefract`) and the interior bends at the glass walls."],"see":["volume.volumeMarch","volume.parallaxPlane","volume.turbulentMedium"],"import":{"statement":"import {volume} from 'shaders/std'","usage":"volume.interiorRay"},"usedBy":["Nebula"],"documented":true,"source":{"file":"std/paint/volume.ts","line":48},"kind":"function","signature":"interiorRay(field: SurfaceField, frame: SurfaceFrame, opts: {origin: {x: Expr; y: Expr}; view?: Expr; shear?: number; lengthScale?: number}): InteriorRay","params":[{"name":"field","type":"SurfaceField","optional":false},{"name":"frame","type":"SurfaceFrame","optional":false},{"name":"opts","type":"{origin: {x: Expr; y: Expr}; view?: Expr; shear?: number; lengthScale?: number}","optional":false}]},"volume.parallaxPlane":{"id":"volume.parallaxPlane","name":"parallaxPlane","qualifiedName":"parallaxPlane","category":"volume","module":"std/paint/volume.ts","summary":"A flat layer at a fixed depth inside the shape, like scenery seen through a window.","description":"`depth` is a fraction of the ray's length: 0 at the entry, 1 at the far side, beyond 1 for a backdrop behind the shape. Give it a `pan` (the `rotationSensor` of the frame) and the layer slides as the shape rotates, `panRate` times as far. Deeper layers with higher rates give the parallax of real depth. Returns the position to sample content at, plus its depth `z`.","examples":["const far = parallaxPlane(ray, {depth: 1.1, pan: rotationSensor(frame), panRate: 1.3, hint: 'far'})"],"tips":["Two planes at different depths and rates are enough to sell a starfield."],"see":["volume.interiorRay","volume.volumeMarch"],"import":{"statement":"import {volume} from 'shaders/std'","usage":"volume.parallaxPlane"},"usedBy":["Nebula"],"documented":true,"source":{"file":"std/paint/volume.ts","line":94},"kind":"function","signature":"parallaxPlane(ray: InteriorRay, opts: {depth: number; pan?: {x: Expr; y: Expr}; panRate?: number; hint?: string}): {x: Expr; y: Expr; z: Expr}","params":[{"name":"ray","type":"InteriorRay","optional":false},{"name":"opts","type":"{depth: number; pan?: {x: Expr; y: Expr}; panRate?: number; hint?: string}","optional":false}]},"volume.turbulentMedium":{"id":"volume.turbulentMedium","name":"turbulentMedium","qualifiedName":"turbulentMedium","category":"volume","module":"std/paint/volume.ts","summary":"A billowing cloud density at a point inside the shape.","description":"`frequency` sets how many cloud cells fit across the shape. `drift` is a time value that moves the clouds. `seed` moves to a different part of the cloud so two instances differ. `billow` (0–1) folds the clouds over themselves for a cumulus look. `density` is signed, roughly −1 to 1: threshold it with `smoothstep` to make clouds with clear gaps. `mid` and `fine` are two finer layers of the same cloud, free to use for dust bands or wisps.","examples":["const gas = turbulentMedium({x: pos.x, y: pos.y, z}, {frequency: math.mul(u.gasScale, 3), drift: t, billow: math.float(0.4)})"],"tips":["Inside a `volumeMarch`, pass the step number as `hint` so each step gets its own values."],"see":["volume.volumeMarch","volume.interiorRay"],"import":{"statement":"import {volume} from 'shaders/std'","usage":"volume.turbulentMedium"},"usedBy":["Nebula"],"documented":true,"source":{"file":"std/paint/volume.ts","line":127},"kind":"function","signature":"turbulentMedium(p: {x: Expr; y: Expr; z: Expr}, opts: {frequency: Expr; drift?: Expr; seed?: Expr; billow?: Expr; hint?: string}): {density: Expr; mid: Expr; fine: Expr}","params":[{"name":"p","type":"{x: Expr; y: Expr; z: Expr}","optional":false},{"name":"opts","type":"{frequency: Expr; drift?: Expr; seed?: Expr; billow?: Expr; hint?: string}","optional":false}]},"volume.volumeMarch":{"id":"volume.volumeMarch","name":"volumeMarch","qualifiedName":"volumeMarch","category":"volume","module":"std/paint/volume.ts","summary":"Walk a ray through the medium front to back, adding up what glows and what dims.","description":"`steps` is how many samples to take (4–8 is typical; it is fixed when the shader compiles). `entry` and `length` come from `interiorRay`. For each step your callback returns `emit`, the rgb light this sample gives off, and `absorb`, the rgb amount it blocks. Absorption is per channel, so a medium can dim blue more than red. Returns the accumulated `color` and the `transmittance` left at the far side: multiply anything behind the medium by it. `jitter` is a small per-pixel offset along the ray, as a fraction of its length (a hash times `1 / steps` works), that trades banding for fine grain. `emissionGain` scales every sample's glow; use `k / steps` so brightness does not change with the step count.","examples":["const march = volumeMarch({steps: 6, entry: ray.entry, length: ray.length, jitter}, ({z, i}) => ({emit: math.mul(rgb, dens), absorb: math.splat3(math.mul(dens, u.density))}))"],"tips":["Fewer steps plus `jitter` beats more steps: the noise becomes grain instead of bands."],"see":["volume.interiorRay","volume.turbulentMedium","volume.parallaxPlane"],"import":{"statement":"import {volume} from 'shaders/std'","usage":"volume.volumeMarch"},"usedBy":["Nebula"],"documented":true,"source":{"file":"std/paint/volume.ts","line":194},"kind":"function","signature":"volumeMarch(opts: {\n        steps: number\n        entry: Expr\n        length: Expr\n        /** A per-pixel offset added to each step's position along the ray, as a fraction of the ray. */\n        jitter?: Expr\n        /** A multiplier on every sample's `emit`. */\n        emissionGain?: Expr\n        hint?: string\n    }, sample: (step: VolumeMarchStep) => {emit: Expr; absorb: Expr}): {color: Expr; transmittance: Expr}","params":[{"name":"opts","type":"{\n        steps: number\n        entry: Expr\n        length: Expr\n        /** A per-pixel offset added to each step's position along the ray, as a fraction of the ray. */\n        jitter?: Expr\n        /** A multiplier on every sample's `emit`. */\n        emissionGain?: Expr\n        hint?: string\n    }","optional":false},{"name":"sample","type":"(step: VolumeMarchStep) => {emit: Expr; absorb: Expr}","optional":false}]},"radiance.gatherIrradiance":{"id":"radiance.gatherIrradiance","name":"gatherIrradiance","qualifiedName":"gatherIrradiance","category":"radiance","module":"std/paint/radiance.ts","summary":"The light arriving at a pixel from a glowing shape edge, with the shape's body blocking it.","description":"Looks out from the pixel in `cones` directions, finds where each touches the shape, and averages the `emission` there. Edges facing away or hidden behind the body contribute nothing. Returns one number: the brightness at this pixel. It runs per pixel per frame, so prefer `irradianceField` unless the light must be live.","examples":["const light = gatherIrradiance({origin: frame.sdfUV!, field: far.at, cones: 16, steps: 24, reach: 3, start: far.sdf, surfaceEps: px, jitter: hash, emission: ({normal}) => math.max(math.dot(normal, lightDir), 0), deps: [lightDir]})"],"tips":["Any value computed outside `emission` and read inside it must be listed in `deps`."],"see":["radiance.irradianceField","radiance.shadowVisibility"],"import":{"statement":"import {radiance} from 'shaders/std'","usage":"radiance.gatherIrradiance"},"usedBy":[],"documented":true,"source":{"file":"std/paint/radiance.ts","line":95},"kind":"function","signature":"gatherIrradiance(spec: GatherSpec): Expr","params":[{"name":"spec","type":"GatherSpec","optional":false}]},"radiance.shadowVisibility":{"id":"radiance.shadowVisibility","name":"shadowVisibility","qualifiedName":"shadowVisibility","category":"radiance","module":"std/paint/radiance.ts","summary":"How much of a light reaches a point, 1 when nothing is in the way and 0 when the shape blocks it.","description":"Casts a ray from `from` toward the light and stops at the shape. Values between 0 and 1 are the soft edge of the shadow, wider with `softness`. Multiply a light's contribution by it.","examples":["const lit = math.mul(strength, shadowVisibility({from: math.add(edge.point, math.mul(edge.normal, math.mul(px, 2))), toward: light.uv, field: far.at, steps: 12, surfaceEps: px, softness: u.shadowSoftness}))"],"tips":["Start `from` two pixels off the edge along its normal or the point shadows itself."],"see":["radiance.gatherIrradiance","radiance.irradianceField"],"import":{"statement":"import {radiance} from 'shaders/std'","usage":"radiance.shadowVisibility"},"usedBy":["Irradiance"],"documented":true,"source":{"file":"std/paint/radiance.ts","line":208},"kind":"function","signature":"shadowVisibility(spec: ShadowSpec): Expr","params":[{"name":"spec","type":"ShadowSpec","optional":false}]},"radiance.irradianceField":{"id":"radiance.irradianceField","name":"irradianceField","qualifiedName":"irradianceField","category":"radiance","module":"std/paint/radiance.ts","summary":"The light spilling from a shape's edges, gathered once into a texture and read per pixel.","description":"Returns two halves. Put `compute` on your definition's `compute:` field. In your material, call `sample(params)` for the rgb light at the pixel. It returns `undefined` when there is no GPU to gather on, so write `sample(params) ?? splat3(0)`. It reads your standard shape props (`center`, `scale`, `rotation`, `shape`, `shapeSdfUrl`, `shapeType`) plus the props named in the spec. The texture only re-gathers when a light or the shape changes.","examples":["const spill = irradianceField(GATHER).sample(params) ?? math.splat3(0)"],"tips":["Build the spec once as a constant and use it for both `compute:` and `sample`."],"see":["radiance.gatherIrradiance","radiance.shadowVisibility"],"import":{"statement":"import {radiance} from 'shaders/std'","usage":"radiance.irradianceField"},"usedBy":["Irradiance"],"documented":true,"source":{"file":"std/paint/radiance.ts","line":375},"kind":"function","signature":"irradianceField(spec: IrradianceFieldSpec): {\n    compute: GpuComputeNode\n    sample: (params: GpuFragmentParams) => Expr | undefined\n}","params":[{"name":"spec","type":"IrradianceFieldSpec","optional":false}]},"voxels.voxelSurface":{"id":"voxels.voxelSurface","name":"voxelSurface","qualifiedName":"voxelSurface","category":"voxels","module":"std/paint/voxels.ts","summary":"Rebuild the shape from blocks and shade each block with your `surface`.","description":"Returns `{extraFields, compute, gpu}` to spread into a shape-effect definition. Your props must include the standard shape props (`center`, `scale`, `rotation`, `shape`, `shapeSdfUrl`, `shapeType`), a `shadows` amount (0–1) and everything the spec names. Your `surface` gets a `VoxelFrame` per block and returns `vec4(rgb, vox.coverage)`. Geometry props re-build the model when they change. Color and light props never do.","examples":["...voxelSurface({style: p('voxelShape'), gridSpace: p('gridSpace'), voxelSize: p('voxelSize'), fill: p('fill'), voxelScale: p('voxelScale'), bevel: p('bevel'), shadowSoftness: p('shadowSoftness'), lightAngle: p('lightAngle'), lightElevation: p('lightElevation'), depth: p('depth'), surface: (vox, frame, params) => math.vec4(shade(vox), vox.coverage)})"],"tips":["Light with `materials.lambert(vox.normal, L, {wrap: 0.12})` times `1 − vox.shadow · shadows` and the blocks read as solid."],"see":["materials.shapedSurface","materials.lambert"],"import":{"statement":"import {voxels} from 'shaders/std'","usage":"voxels.voxelSurface"},"usedBy":["Voxels"],"documented":true,"source":{"file":"std/paint/voxels.ts","line":142},"kind":"function","signature":"voxelSurface(spec: VoxelSurfaceSpec): ShapedSurfaceEffect","params":[{"name":"spec","type":"VoxelSurfaceSpec","optional":false}]},"shape.circle":{"id":"shape.circle","name":"circle","qualifiedName":"circle","category":"shape","module":"std/shape.ts","summary":"A circle of a given radius.","description":"`radius` is in uv (1 = the canvas height). Returns the signed distance to the edge, negative inside.","examples":["shape: {distance: ({x, y}, u) => circle(x, y, u('radius')), bounds: {size: {width: 'radius', height: 'radius'}}, …}"],"tips":["Blend a sharp shape toward this one with `math.mix` to round it off (the Polygon shader does)."],"see":["shape.ellipse","shape.ring","shape.polygon"],"import":{"statement":"import {shape} from 'shaders/std'","usage":"shape.circle"},"usedBy":["Circle","Polygon","Waveform"],"documented":true,"source":{"file":"std/shape.ts","line":44},"kind":"function","signature":"circle(x: Val, y: Val, radius: Val): Expr","params":[{"name":"x","type":"Val","optional":false},{"name":"y","type":"Val","optional":false},{"name":"radius","type":"Val","optional":false}]},"shape.ellipse":{"id":"shape.ellipse","name":"ellipse","qualifiedName":"ellipse","category":"shape","module":"std/shape.ts","summary":"An ellipse with separate horizontal and vertical radii.","description":"`radiusX` and `radiusY` are the half-widths along each axis, in uv.","examples":["shape: {distance: ({x, y}, u) => ellipse(x, y, u('radiusX'), u('radiusY')), …}"],"tips":[],"see":["shape.circle","shape.roundedRect"],"import":{"statement":"import {shape} from 'shaders/std'","usage":"shape.ellipse"},"usedBy":["Ellipse"],"documented":true,"source":{"file":"std/shape.ts","line":57},"kind":"function","signature":"ellipse(x: Val, y: Val, radiusX: Val, radiusY: Val): Expr","params":[{"name":"x","type":"Val","optional":false},{"name":"y","type":"Val","optional":false},{"name":"radiusX","type":"Val","optional":false},{"name":"radiusY","type":"Val","optional":false}]},"shape.roundedRect":{"id":"shape.roundedRect","name":"roundedRect","qualifiedName":"roundedRect","category":"shape","module":"std/shape.ts","summary":"A rectangle with rounded corners.","description":"`width` and `height` are half-extents (the distance from the centre to each side), in uv. `rounding` is the corner radius. Pass 0 for a sharp rectangle.","examples":["shape: {distance: ({x, y}, u) => roundedRect(x, y, u('width'), u('height'), u('rounding')), …}"],"tips":["A `width` of 0.5 spans the full canvas height, not half of it."],"see":["shape.parallelogram","shape.trapezoid","shape.cross"],"import":{"statement":"import {shape} from 'shaders/std'","usage":"shape.roundedRect"},"usedBy":["RoundedRect","Waveform"],"documented":true,"source":{"file":"std/shape.ts","line":72},"kind":"function","signature":"roundedRect(x: Val, y: Val, width: Val, height: Val, rounding: Val): Expr","params":[{"name":"x","type":"Val","optional":false},{"name":"y","type":"Val","optional":false},{"name":"width","type":"Val","optional":false},{"name":"height","type":"Val","optional":false},{"name":"rounding","type":"Val","optional":false}]},"shape.polygon":{"id":"shape.polygon","name":"polygon","qualifiedName":"polygon","category":"shape","module":"std/shape.ts","summary":"A regular polygon with any number of sides.","description":"`radius` is the distance from the centre to the middle of each side (not to the corners), in uv. `sides` is the side count, 3 for a triangle and up. A flat side faces right at rotation 0.","examples":["shape: {distance: ({x, y}, u) => polygon(x, y, u('radius'), u('sides')), …}"],"tips":["`math.mix(polygon(…), circle(…), u('rounding'))` morphs the corners away."],"see":["shape.star","shape.flower","shape.circle"],"import":{"statement":"import {shape} from 'shaders/std'","usage":"shape.polygon"},"usedBy":["Polygon"],"documented":true,"source":{"file":"std/shape.ts","line":87},"kind":"function","signature":"polygon(x: Val, y: Val, radius: Val, sides: Val): Expr","params":[{"name":"x","type":"Val","optional":false},{"name":"y","type":"Val","optional":false},{"name":"radius","type":"Val","optional":false},{"name":"sides","type":"Val","optional":false}]},"shape.star":{"id":"shape.star","name":"star","qualifiedName":"star","category":"shape","module":"std/shape.ts","summary":"A star with straight sides and sharp points.","description":"`outerRadius` is the distance from the centre to the tips, in uv. `sides` is the number of points. `innerRatio` (0–1) is the inner corner radius as a fraction of the tip radius. 0.382 gives the classic five-point star. One tip points right at rotation 0.","examples":["shape: {distance: ({x, y}, u) => star(x, y, u('radius'), u('sides'), u('innerRatio')), …}"],"tips":[],"see":["shape.flower","shape.polygon"],"import":{"statement":"import {shape} from 'shaders/std'","usage":"shape.star"},"usedBy":["Star"],"documented":true,"source":{"file":"std/shape.ts","line":102},"kind":"function","signature":"star(x: Val, y: Val, outerRadius: Val, sides: Val, innerRatio: Val): Expr","params":[{"name":"x","type":"Val","optional":false},{"name":"y","type":"Val","optional":false},{"name":"outerRadius","type":"Val","optional":false},{"name":"sides","type":"Val","optional":false},{"name":"innerRatio","type":"Val","optional":false}]},"shape.flower":{"id":"shape.flower","name":"flower","qualifiedName":"flower","category":"shape","module":"std/shape.ts","summary":"A flower with pointed petals and V-shaped valleys between them.","description":"`outerRadius` is the distance from the centre to a petal tip, in uv. `sides` is the petal count. `innerRatio` (0–1) is the valley radius as a fraction of the tip radius.","examples":["shape: {distance: ({x, y}, u) => flower(x, y, u('radius'), u('sides'), u('innerRatio')), …}"],"tips":[],"see":["shape.star","shape.polygon"],"import":{"statement":"import {shape} from 'shaders/std'","usage":"shape.flower"},"usedBy":["Flower"],"documented":true,"source":{"file":"std/shape.ts","line":116},"kind":"function","signature":"flower(x: Val, y: Val, outerRadius: Val, sides: Val, innerRatio: Val): Expr","params":[{"name":"x","type":"Val","optional":false},{"name":"y","type":"Val","optional":false},{"name":"outerRadius","type":"Val","optional":false},{"name":"sides","type":"Val","optional":false},{"name":"innerRatio","type":"Val","optional":false}]},"shape.heart":{"id":"shape.heart","name":"heart","qualifiedName":"heart","category":"shape","module":"std/shape.ts","summary":"A heart, point down at rotation 0.","description":"`radius` is the size it is fitted into, in uv.","examples":["shape: {distance: ({x, y}, u) => heart(x, y, u('radius')), bounds: {size: {width: 'radius', height: 'radius'}}, …}"],"tips":[],"see":["shape.teardrop","shape.circle"],"import":{"statement":"import {shape} from 'shaders/std'","usage":"shape.heart"},"usedBy":["Heart"],"documented":true,"source":{"file":"std/shape.ts","line":129},"kind":"function","signature":"heart(x: Val, y: Val, radius: Val): Expr","params":[{"name":"x","type":"Val","optional":false},{"name":"y","type":"Val","optional":false},{"name":"radius","type":"Val","optional":false}]},"shape.cross":{"id":"shape.cross","name":"cross","qualifiedName":"cross","category":"shape","module":"std/shape.ts","summary":"A plus-shaped cross with four equal arms.","description":"`size` is the arm half-length (centre to an arm's end), `thickness` the arm half-width, both in uv. `rounding` rounds the corners. Rotate it 45° for an X.","examples":["shape: {distance: ({x, y}, u) => cross(x, y, u('radius'), u('thickness'), u('rounding')), …}"],"tips":[],"see":["shape.roundedRect"],"import":{"statement":"import {shape} from 'shaders/std'","usage":"shape.cross"},"usedBy":["Cross"],"documented":true,"source":{"file":"std/shape.ts","line":143},"kind":"function","signature":"cross(x: Val, y: Val, size: Val, thickness: Val, rounding: Val): Expr","params":[{"name":"x","type":"Val","optional":false},{"name":"y","type":"Val","optional":false},{"name":"size","type":"Val","optional":false},{"name":"thickness","type":"Val","optional":false},{"name":"rounding","type":"Val","optional":false}]},"shape.ring":{"id":"shape.ring","name":"ring","qualifiedName":"ring","category":"shape","module":"std/shape.ts","summary":"A ring: a circular band around a centre.","description":"`radius` is the distance to the middle of the band, `thickness` half the band's width, both in uv. The full band is twice `thickness` wide.","examples":["shape: {distance: ({x, y}, u) => ring(x, y, u('radius'), u('thickness')), rotatable: false, …}"],"tips":["Rotationally symmetric, so declare `rotatable: false` and the rotation prop is left out."],"see":["shape.circle","shape.arc","shape.crescent"],"import":{"statement":"import {shape} from 'shaders/std'","usage":"shape.ring"},"usedBy":["Ring"],"documented":true,"source":{"file":"std/shape.ts","line":158},"kind":"function","signature":"ring(x: Val, y: Val, radius: Val, thickness: Val): Expr","params":[{"name":"x","type":"Val","optional":false},{"name":"y","type":"Val","optional":false},{"name":"radius","type":"Val","optional":false},{"name":"thickness","type":"Val","optional":false}]},"shape.vesica":{"id":"shape.vesica","name":"vesica","qualifiedName":"vesica","category":"shape","module":"std/shape.ts","summary":"A vesica: the lens where two equal circles overlap.","description":"`radius` is each circle's radius, in uv. `spread` (0–1) is how far apart the circles sit as a fraction of the radius. 0 is a full circle, 1 an infinitely thin lens.","examples":["shape: {distance: ({x, y}, u) => vesica(x, y, u('radius'), u('spread')), …}"],"tips":[],"see":["shape.ellipse","shape.crescent"],"import":{"statement":"import {shape} from 'shaders/std'","usage":"shape.vesica"},"usedBy":["Vesica"],"documented":true,"source":{"file":"std/shape.ts","line":172},"kind":"function","signature":"vesica(x: Val, y: Val, radius: Val, spread: Val): Expr","params":[{"name":"x","type":"Val","optional":false},{"name":"y","type":"Val","optional":false},{"name":"radius","type":"Val","optional":false},{"name":"spread","type":"Val","optional":false}]},"shape.crescent":{"id":"shape.crescent","name":"crescent","qualifiedName":"crescent","category":"shape","module":"std/shape.ts","summary":"A crescent: a disc with a smaller disc bitten out of it.","description":"`outerRadius` is the full disc's radius, in uv. `innerRatio` (0–1) is the bite's radius as a fraction of it. `offset` is how far the bite's centre is shifted sideways, in uv.","examples":["shape: {distance: ({x, y}, u) => crescent(x, y, u('radius'), u('innerRatio'), u('offset')), …}"],"tips":[],"see":["shape.ring","shape.vesica"],"import":{"statement":"import {shape} from 'shaders/std'","usage":"shape.crescent"},"usedBy":["Crescent"],"documented":true,"source":{"file":"std/shape.ts","line":186},"kind":"function","signature":"crescent(x: Val, y: Val, outerRadius: Val, innerRatio: Val, offset: Val): Expr","params":[{"name":"x","type":"Val","optional":false},{"name":"y","type":"Val","optional":false},{"name":"outerRadius","type":"Val","optional":false},{"name":"innerRatio","type":"Val","optional":false},{"name":"offset","type":"Val","optional":false}]},"shape.trapezoid":{"id":"shape.trapezoid","name":"trapezoid","qualifiedName":"trapezoid","category":"shape","module":"std/shape.ts","summary":"A trapezoid: a four-sided shape with a flat top and bottom of different widths.","description":"`topWidth` and `bottomWidth` are half-widths of the top and bottom edges as seen on screen, `height` the half-height, all in uv. Equal widths give a rectangle.","examples":["shape: {distance: ({x, y}, u) => trapezoid(x, y, u('topWidth'), u('bottomWidth'), u('height')), …}"],"tips":[],"see":["shape.parallelogram","shape.roundedRect"],"import":{"statement":"import {shape} from 'shaders/std'","usage":"shape.trapezoid"},"usedBy":["Trapezoid"],"documented":true,"source":{"file":"std/shape.ts","line":200},"kind":"function","signature":"trapezoid(x: Val, y: Val, topWidth: Val, bottomWidth: Val, height: Val): Expr","params":[{"name":"x","type":"Val","optional":false},{"name":"y","type":"Val","optional":false},{"name":"topWidth","type":"Val","optional":false},{"name":"bottomWidth","type":"Val","optional":false},{"name":"height","type":"Val","optional":false}]},"shape.teardrop":{"id":"shape.teardrop","name":"teardrop","qualifiedName":"teardrop","category":"shape","module":"std/shape.ts","summary":"A teardrop: a round bulb tapering to a sharp point, point up at rotation 0.","description":"`radius` is the bulb's radius and `height` the distance from the bulb's centre to the point, both in uv.","examples":["shape: {distance: ({x, y}, u) => teardrop(x, y, u('radius'), u('height')), …}"],"tips":[],"see":["shape.heart","shape.ellipse"],"import":{"statement":"import {shape} from 'shaders/std'","usage":"shape.teardrop"},"usedBy":["Teardrop"],"documented":true,"source":{"file":"std/shape.ts","line":214},"kind":"function","signature":"teardrop(x: Val, y: Val, radius: Val, height: Val): Expr","params":[{"name":"x","type":"Val","optional":false},{"name":"y","type":"Val","optional":false},{"name":"radius","type":"Val","optional":false},{"name":"height","type":"Val","optional":false}]},"shape.parallelogram":{"id":"shape.parallelogram","name":"parallelogram","qualifiedName":"parallelogram","category":"shape","module":"std/shape.ts","summary":"A parallelogram: a rectangle with its top edge slid sideways.","description":"`width` and `height` are half-extents, in uv. `skew` is how far the top edge is shifted horizontally, in uv. 0 gives a rectangle.","examples":["shape: {distance: ({x, y}, u) => parallelogram(x, y, u('width'), u('height'), u('skew')), …}"],"tips":[],"see":["shape.trapezoid","shape.roundedRect"],"import":{"statement":"import {shape} from 'shaders/std'","usage":"shape.parallelogram"},"usedBy":["Parallelogram"],"documented":true,"source":{"file":"std/shape.ts","line":228},"kind":"function","signature":"parallelogram(x: Val, y: Val, width: Val, height: Val, skew: Val): Expr","params":[{"name":"x","type":"Val","optional":false},{"name":"y","type":"Val","optional":false},{"name":"width","type":"Val","optional":false},{"name":"height","type":"Val","optional":false},{"name":"skew","type":"Val","optional":false}]},"shape.arc":{"id":"shape.arc","name":"arc","qualifiedName":"arc","category":"shape","module":"std/shape.ts","summary":"A pie wedge: a filled sector of a circle.","description":"`radius` is the sector's radius, in uv. `halfAngle` is half the opening angle, in radians. Multiply an aperture prop in degrees by `APERTURE_TO_HALF_ANGLE` to get it. The wedge opens downward at rotation 0.","examples":["shape: {distance: ({x, y}, u) => arc(x, y, u('radius'), math.mul(u('aperture'), APERTURE_TO_HALF_ANGLE)), …}"],"tips":[],"see":["shape.ring","shape.circle","shape.APERTURE_TO_HALF_ANGLE"],"import":{"statement":"import {shape} from 'shaders/std'","usage":"shape.arc"},"usedBy":["Arc"],"documented":true,"source":{"file":"std/shape.ts","line":243},"kind":"function","signature":"arc(x: Val, y: Val, radius: Val, halfAngle: Val): Expr","params":[{"name":"x","type":"Val","optional":false},{"name":"y","type":"Val","optional":false},{"name":"radius","type":"Val","optional":false},{"name":"halfAngle","type":"Val","optional":false}]},"shape.APERTURE_TO_HALF_ANGLE":{"id":"shape.APERTURE_TO_HALF_ANGLE","name":"APERTURE_TO_HALF_ANGLE","qualifiedName":"APERTURE_TO_HALF_ANGLE","category":"shape","module":"std/shape.ts","summary":"The factor that turns an opening angle in degrees into the half-angle `arc` takes.","description":"Multiply a 0–360 aperture prop by it. It equals π/360.","examples":["distance: ({x, y}, u) => arc(x, y, u('radius'), math.mul(u('aperture'), APERTURE_TO_HALF_ANGLE))"],"tips":[],"see":["shape.arc"],"import":{"statement":"import {shape} from 'shaders/std'","usage":"shape.APERTURE_TO_HALF_ANGLE"},"usedBy":["Arc"],"documented":true,"source":{"file":"std/shape.ts","line":256},"kind":"constant","signature":"APERTURE_TO_HALF_ANGLE: 0.008726646259971648","params":[]},"frames.surfaceOf":{"id":"frames.surfaceOf","name":"surfaceOf","qualifiedName":"surfaceOf","category":"frames","module":"std/frames.ts","summary":"The canvas a generator paints on: its `uv`, its `viewport` size in pixels and its `aspect`.","description":"`uv` runs 0–1 across the layer, y down. `aspect` is width over height. When a parent fits the layer into a box, `viewport` and `aspect` describe that box. Every frame takes this as its second argument.","examples":["paint: (params) => { const {uv, viewport, aspect} = surfaceOf(params); … }"],"tips":[],"see":["frames.rawSurfaceOf","frames.centredFrame","frames.segmentFrame"],"import":{"statement":"import {frames} from 'shaders/std'","usage":"frames.surfaceOf"},"usedBy":["Aurora","FlowingGradient","Marble","MeshGradient","Prism","Satin","StudioBackground","Waveform"],"documented":true,"source":{"file":"std/frames.ts","line":35},"kind":"function","signature":"surfaceOf(params: GpuFragmentParams): {uv: Expr; viewport: Expr; aspect: Expr}","params":[{"name":"params","type":"GpuFragmentParams","optional":false}]},"frames.rawSurfaceOf":{"id":"frames.rawSurfaceOf","name":"rawSurfaceOf","qualifiedName":"rawSurfaceOf","category":"frames","module":"std/frames.ts","summary":"The canvas like `surfaceOf`, but always measured against the whole canvas, never a fitted box.","description":"Use it when a pattern should stay anchored to the canvas as the layer is resized.","examples":["const {delta, dist} = centredFrame({center: p('center')})(params, rawSurfaceOf(params))"],"tips":[],"see":["frames.surfaceOf"],"import":{"statement":"import {frames} from 'shaders/std'","usage":"frames.rawSurfaceOf"},"usedBy":["Blob"],"documented":true,"source":{"file":"std/frames.ts","line":55},"kind":"function","signature":"rawSurfaceOf(params: GpuFragmentParams): {uv: Expr; viewport: Expr; aspect: Expr}","params":[{"name":"params","type":"GpuFragmentParams","optional":false}]},"frames.centredFrame":{"id":"frames.centredFrame","name":"centredFrame","qualifiedName":"centredFrame","category":"frames","module":"std/frames.ts","summary":"A frame centred on a position prop: each pixel's offset from the centre and its distance.","description":"Pass `{center: p('center')}` where `center` is a position prop. Call the result with `params` and a surface. Distances are aspect-corrected, so 1 is the canvas height and a circle stays round on a wide canvas.","examples":["const {dist} = centredFrame({center: p('center')})(params, surfaceOf(params))"],"tips":["`dist` is in canvas-height units: a radius of 0.5 reaches the top and bottom edges from the middle."],"see":["frames.segmentFrame","frames.surfaceOf","frames.rawSurfaceOf"],"import":{"statement":"import {frames} from 'shaders/std'","usage":"frames.centredFrame"},"usedBy":["Blob"],"documented":true,"source":{"file":"std/frames.ts","line":86},"kind":"function","signature":"centredFrame(slots: {center: PropRef}): (params: GpuFragmentParams, surface: { uv: Expr; aspect: Expr; }) => CentredFrame","params":[{"name":"slots","type":"{center: PropRef}","optional":false}]},"frames.segmentFrame":{"id":"frames.segmentFrame","name":"segmentFrame","qualifiedName":"segmentFrame","category":"frames","module":"std/frames.ts","summary":"A frame along the line between two position props: how far each pixel is along it and across it.","description":"Pass `{from: p('from'), to: p('to')}` and call the result with `params` and a surface. `along` is 0 at `from` and equals `length` at `to`. `across` is the signed distance from the line. All three are aspect-corrected, in canvas-height units.","examples":["const {along, across, length} = segmentFrame({from: p('from'), to: p('to')})(params, surfaceOf(params))"],"tips":["Divide `along` by `length` for a 0–1 position along the line, the natural gradient parameter."],"see":["frames.centredFrame","frames.directionFrame","frames.surfaceOf"],"import":{"statement":"import {frames} from 'shaders/std'","usage":"frames.segmentFrame"},"usedBy":["Prism","Waveform"],"documented":true,"source":{"file":"std/frames.ts","line":125},"kind":"function","signature":"segmentFrame(slots: {from: PropRef; to: PropRef}): (params: GpuFragmentParams, surface: { uv: Expr; aspect: Expr; }) => SegmentFrame","params":[{"name":"slots","type":"{from: PropRef; to: PropRef}","optional":false}]},"frames.direction":{"id":"frames.direction","name":"direction","qualifiedName":"direction","category":"frames","module":"std/frames.ts","summary":"A direction in degrees as a unit vector.","description":"0° points right and angles turn clockwise on screen, so 90° points down. Use it wherever a word wants a light or flow direction as a vector.","examples":["const light = direction(params.uniforms.lightAngle, 'light')"],"tips":[],"see":["frames.directionFrame"],"import":{"statement":"import {frames} from 'shaders/std'","usage":"frames.direction"},"usedBy":["BrushedMetal","CarbonFiber","Frost","Goo","LiquidMetal","Nebula","Obsidian","Plastic","Satin","Water"],"documented":true,"source":{"file":"std/frames.ts","line":165},"kind":"function","signature":"direction(angleDeg: Expr | number, hint: string = 'dir'): Expr","params":[{"name":"angleDeg","type":"Expr | number","optional":false},{"name":"hint","type":"string","optional":true,"defaultValue":"'dir'"}]},"frames.directionFrame":{"id":"frames.directionFrame","name":"directionFrame","qualifiedName":"directionFrame","category":"frames","module":"std/frames.ts","summary":"The two axes of a direction given in degrees, for patterns that run along it.","description":"`tangent` points along the direction, `perp` at right angles to it. `coordsOf(point)` returns that point's `along` and `across` distances in the frame. Brush grain, weave, streaks and flow all live in one of these.","examples":["const grain = directionFrame(params.uniforms.brushAngle, 'brush'); const {along, across} = grain.coordsOf(delta)"],"tips":[],"see":["frames.direction","frames.segmentFrame"],"import":{"statement":"import {frames} from 'shaders/std'","usage":"frames.directionFrame"},"usedBy":["BrushedMetal","CarbonFiber","Heatmap","Obsidian","Satin"],"documented":true,"source":{"file":"std/frames.ts","line":194},"kind":"function","signature":"directionFrame(angleDeg: Expr | number, hint: string = 'grain'): DirectionFrame","params":[{"name":"angleDeg","type":"Expr | number","optional":false},{"name":"hint","type":"string","optional":true,"defaultValue":"'grain'"}]},"frames.sectorFold":{"id":"frames.sectorFold","name":"sectorFold","qualifiedName":"sectorFold","category":"frames","module":"std/frames.ts","summary":"Fold a point into one mirrored wedge around a centre: the kaleidoscope fold.","description":"`sectorAngle` is the wedge's angle in radians (2π divided by the number of facets). `center` is where the wedges meet, 0.5 by default. Returns the folded point as an offset from the centre. Sample anything at that point and it repeats and mirrors around the centre.","examples":["const folded = sectorFold(uv.member('x'), uv.member('y'), math.div(2 * Math.PI, params.uniforms.facets))"],"tips":[],"see":["frames.directionFrame","frames.centredFrame"],"import":{"statement":"import {frames} from 'shaders/std'","usage":"frames.sectorFold"},"usedBy":["Crystal"],"documented":true,"source":{"file":"std/frames.ts","line":223},"kind":"function","signature":"sectorFold(x: Expr, y: Expr, sectorAngle: Expr, center: number = 0.5): Expr","params":[{"name":"x","type":"Expr","optional":false},{"name":"y","type":"Expr","optional":false},{"name":"sectorAngle","type":"Expr","optional":false},{"name":"center","type":"number","optional":true,"defaultValue":"0.5"}]},"warps.mirrorLine":{"id":"warps.mirrorLine","name":"mirrorLine","qualifiedName":"mirrorLine","category":"warps","module":"std/warps.ts","summary":"Mirror the child across a line through `center` at `angle`.","description":"`center` is in uv, `angle` in degrees. One side of the line stays as it is and the other side shows its reflection.","examples":["map: warps.mirrorLine({center: p('center'), angle: p('angle')})"],"tips":["Pair it with an `edges` prop that defaults to `'mirror'` so the reflected half never runs out of picture."],"see":["warps.flip","warps.kaleidoscope"],"import":{"statement":"import {warps} from 'shaders/std'","usage":"warps.mirrorLine"},"usedBy":["Mirror"],"documented":true,"source":{"file":"std/warps.ts","line":61},"kind":"function","signature":"mirrorLine(slots: {center: ArgSpec; angle: ArgSpec}): UvMapSource","params":[{"name":"slots","type":"{center: ArgSpec; angle: ArgSpec}","optional":false}]},"warps.flip":{"id":"warps.flip","name":"flip","qualifiedName":"flip","category":"warps","module":"std/warps.ts","summary":"Flip the child horizontally, vertically, or both.","description":"`flipX` and `flipY` are boolean props declared with `transform: transformBoolean`. The flip never looks outside the child, so set `edges: 'none'` on the definition.","examples":["map: warps.flip({flipX: p('flipX'), flipY: p('flipY')}),\nedges: 'none',"],"tips":[],"see":["warps.mirrorLine"],"import":{"statement":"import {warps} from 'shaders/std'","usage":"warps.flip"},"usedBy":["Flip"],"documented":true,"source":{"file":"std/warps.ts","line":94},"kind":"function","signature":"flip(slots: {flipX: ArgSpec; flipY: ArgSpec}): UvMapSource","params":[{"name":"slots","type":"{flipX: ArgSpec; flipY: ArgSpec}","optional":false}]},"warps.bend":{"id":"warps.bend","name":"bend","qualifiedName":"bend","category":"warps","module":"std/warps.ts","summary":"Curve the child like a bent sheet seen in perspective, so its ends swell or shrink.","description":"`strength` runs −1 to 1 and its sign picks the bend direction. `angle` is the bend axis in degrees. `falloff` (0–1) keeps more of the middle flat and pushes the bend toward the ends. Where the sheet leaves the frame there is nothing to show, so pair it with an `edges` prop that defaults to `'transparent'`.","examples":["map: warps.bend({strength: p('strength'), falloff: p('falloff'), angle: p('angle')})"],"tips":[],"see":["warps.perspective","warps.cornerPin"],"import":{"statement":"import {warps} from 'shaders/std'","usage":"warps.bend"},"usedBy":["Bend"],"documented":true,"source":{"file":"std/warps.ts","line":117},"kind":"function","signature":"bend(slots: {strength: ArgSpec; falloff: ArgSpec; angle: ArgSpec}): UvMapSource","params":[{"name":"slots","type":"{strength: ArgSpec; falloff: ArgSpec; angle: ArgSpec}","optional":false}]},"warps.bulge":{"id":"warps.bulge","name":"bulge","qualifiedName":"bulge","category":"warps","module":"std/warps.ts","summary":"Magnify or pinch the child inside a disc around `center`.","description":"`strength` runs −1 to 1: positive bulges out, negative pinches in. `radius` sets the size of the disc (1 reaches about half the canvas height). `falloff` runs 0 (hard edge) to 1 (soft).","examples":["map: warps.bulge({center: p('center'), strength: p('strength'), radius: p('radius'), falloff: p('falloff')})"],"tips":[],"see":["warps.twirl","warps.stretch"],"import":{"statement":"import {warps} from 'shaders/std'","usage":"warps.bulge"},"usedBy":["Bulge"],"documented":true,"source":{"file":"std/warps.ts","line":140},"kind":"function","signature":"bulge(slots: {center: ArgSpec; strength: ArgSpec; radius: ArgSpec; falloff: ArgSpec}): UvMapSource","params":[{"name":"slots","type":"{center: ArgSpec; strength: ArgSpec; radius: ArgSpec; falloff: ArgSpec}","optional":false}]},"warps.twirl":{"id":"warps.twirl","name":"twirl","qualifiedName":"twirl","category":"warps","module":"std/warps.ts","summary":"Twist the child around `center`, turning more the farther a pixel is from it.","description":"`intensity` is the twist in radians per unit of distance, so the canvas edge turns by about `intensity` radians. Negative values twist the other way. The library uses −5 to 5.","examples":["map: warps.twirl({center: p('center'), intensity: p('intensity')})"],"tips":[],"see":["warps.bulge","warps.concentricRings"],"import":{"statement":"import {warps} from 'shaders/std'","usage":"warps.twirl"},"usedBy":["Twirl"],"documented":true,"source":{"file":"std/warps.ts","line":162},"kind":"function","signature":"twirl(slots: {center: ArgSpec; intensity: ArgSpec}): UvMapSource","params":[{"name":"slots","type":"{center: ArgSpec; intensity: ArgSpec}","optional":false}]},"warps.kaleidoscope":{"id":"warps.kaleidoscope","name":"kaleidoscope","qualifiedName":"kaleidoscope","category":"warps","module":"std/warps.ts","summary":"Fold the child into mirrored pie slices around `center`.","description":"`segments` is the number of slices (2 or more). `angle` rotates the whole pattern, in degrees. Pair it with an `edges` prop that defaults to `'mirror'`.","examples":["map: warps.kaleidoscope({center: p('center'), segments: p('segments'), angle: p('angle')})"],"tips":[],"see":["warps.mirrorLine","warps.toPolar"],"import":{"statement":"import {warps} from 'shaders/std'","usage":"warps.kaleidoscope"},"usedBy":["Kaleidoscope"],"documented":true,"source":{"file":"std/warps.ts","line":182},"kind":"function","signature":"kaleidoscope(slots: {center: ArgSpec; segments: ArgSpec; angle: ArgSpec}): UvMapSource","params":[{"name":"slots","type":"{center: ArgSpec; segments: ArgSpec; angle: ArgSpec}","optional":false}]},"warps.stretch":{"id":"warps.stretch","name":"stretch","qualifiedName":"stretch","category":"warps","module":"std/warps.ts","summary":"Stretch the child along one direction, starting at `center`.","description":"`angle` is the direction in degrees. Content on the side `angle` points to is pulled out; the other side is left alone. `strength` (0–1) sets how far. `falloff` (0–1) softens the start of the stretch, from a hard line at 0 to a long ramp at 1.","examples":["map: warps.stretch({center: p('center'), strength: p('strength'), angle: p('angle'), falloff: p('falloff')})"],"tips":[],"see":["warps.bulge","warps.bend"],"import":{"statement":"import {warps} from 'shaders/std'","usage":"warps.stretch"},"usedBy":["Stretch"],"documented":true,"source":{"file":"std/warps.ts","line":203},"kind":"function","signature":"stretch(slots: {center: ArgSpec; strength: ArgSpec; angle: ArgSpec; falloff: ArgSpec}): UvMapSource","params":[{"name":"slots","type":"{center: ArgSpec; strength: ArgSpec; angle: ArgSpec; falloff: ArgSpec}","optional":false}]},"warps.wave":{"id":"warps.wave","name":"wave","qualifiedName":"wave","category":"warps","module":"std/warps.ts","summary":"Ripple the child with a travelling wave.","description":"`angle` is the travel direction in degrees, `frequency` the number of waves across the canvas, `strength` (0–1) how far pixels move. `waveType` is a prop marked `compileTime: true` holding `'sine'`, `'triangle'`, `'square'`, `'sawtooth'` or `'bounce'`. The wave moves with the layer's clock, so declare `animatedTime: {speed: 'speed'}`.","examples":["map: warps.wave({strength: p('strength'), frequency: p('frequency'), angle: p('angle'), waveType: p('waveType')})"],"tips":["Changing `waveType` recompiles the shader; the other inputs update live."],"see":["warps.flowNoise","warps.barOffset"],"import":{"statement":"import {warps} from 'shaders/std'","usage":"warps.wave"},"usedBy":["WaveDistortion"],"documented":true,"source":{"file":"std/warps.ts","line":248},"kind":"function","signature":"wave(slots: {strength: ArgSpec; frequency: ArgSpec; angle: ArgSpec; waveType: PropRef}): UvMapSource","params":[{"name":"slots","type":"{strength: ArgSpec; frequency: ArgSpec; angle: ArgSpec; waveType: PropRef}","optional":false}]},"warps.toPolar":{"id":"warps.toPolar","name":"toPolar","qualifiedName":"toPolar","category":"warps","module":"std/warps.ts","summary":"Unroll the child around `center`, so angle becomes x and distance becomes y.","description":"`wrap` scales how many times the child repeats around (1 is once). `radius` scales the distance axis. `intensity` runs 0 (no change) to 1 (the full polar unwrap), and the values between are a real partial warp, not a cross-fade.","examples":["map: warps.toPolar({center: p('center'), wrap: p('wrap'), radius: p('radius'), intensity: p('intensity')})"],"tips":[],"see":["warps.fromPolar","warps.kaleidoscope"],"import":{"statement":"import {warps} from 'shaders/std'","usage":"warps.toPolar"},"usedBy":["PolarCoordinates"],"documented":true,"source":{"file":"std/warps.ts","line":276},"kind":"function","signature":"toPolar(slots: {center: ArgSpec; wrap: ArgSpec; radius: ArgSpec; intensity: ArgSpec}): UvMapSource","params":[{"name":"slots","type":"{center: ArgSpec; wrap: ArgSpec; radius: ArgSpec; intensity: ArgSpec}","optional":false}]},"warps.fromPolar":{"id":"warps.fromPolar","name":"fromPolar","qualifiedName":"fromPolar","category":"warps","module":"std/warps.ts","summary":"Wrap the child into a circle around `center`, reading its x as angle and its y as distance.","description":"The reverse of `toPolar`. `scale` sets how large the circle is. `intensity` runs 0 (no change) to 1 (the full wrap).","examples":["map: warps.fromPolar({center: p('center'), scale: p('scale'), intensity: p('intensity')})"],"tips":[],"see":["warps.toPolar"],"import":{"statement":"import {warps} from 'shaders/std'","usage":"warps.fromPolar"},"usedBy":["RectangularCoordinates"],"documented":true,"source":{"file":"std/warps.ts","line":301},"kind":"function","signature":"fromPolar(slots: {center: ArgSpec; scale: ArgSpec; intensity: ArgSpec}): UvMapSource","params":[{"name":"slots","type":"{center: ArgSpec; scale: ArgSpec; intensity: ArgSpec}","optional":false}]},"warps.cornerPin":{"id":"warps.cornerPin","name":"cornerPin","qualifiedName":"cornerPin","category":"warps","module":"std/warps.ts","summary":"Pin the four corners of the child to new positions, with true perspective between them.","description":"The corners are positions in uv. `amount` (0–1) blends each corner from its normal place (0) to the pinned position (1). Pixels outside the pinned quad are clipped to transparent whatever the edge mode.","examples":["map: warps.cornerPin({topLeft: p('topLeft'), topRight: p('topRight'), bottomRight: p('bottomRight'), bottomLeft: p('bottomLeft'), amount: p('amount')})"],"tips":[],"see":["warps.perspective","warps.bend"],"import":{"statement":"import {warps} from 'shaders/std'","usage":"warps.cornerPin"},"usedBy":["CornerPin"],"documented":true,"source":{"file":"std/warps.ts","line":327},"kind":"function","signature":"cornerPin(slots: {\n    topLeft: ArgSpec\n    topRight: ArgSpec\n    bottomRight: ArgSpec\n    bottomLeft: ArgSpec\n    amount: ArgSpec\n}): UvMapSource","params":[{"name":"slots","type":"{\n    topLeft: ArgSpec\n    topRight: ArgSpec\n    bottomRight: ArgSpec\n    bottomLeft: ArgSpec\n    amount: ArgSpec\n}","optional":false}]},"warps.perspective":{"id":"warps.perspective","name":"perspective","qualifiedName":"perspective","category":"warps","module":"std/warps.ts","summary":"Tilt the child in 3D, as if it were a card turned away from the camera.","description":"`pan` and `tilt` are the turns in degrees (−90 to 90). `fov` is the camera's field of view in degrees: wider means stronger perspective. `zoom` scales the picture (1 is unchanged). `center` is the pivot and `offset` shifts the result, both in uv.","examples":["map: warps.perspective({center: p('center'), pan: p('pan'), tilt: p('tilt'), fov: p('fov'), zoom: p('zoom'), offset: p('offset')})"],"tips":[],"see":["warps.cornerPin","warps.bend"],"import":{"statement":"import {warps} from 'shaders/std'","usage":"warps.perspective"},"usedBy":["Perspective"],"documented":true,"source":{"file":"std/warps.ts","line":362},"kind":"function","signature":"perspective(slots: {\n    center: ArgSpec\n    pan: ArgSpec\n    tilt: ArgSpec\n    fov: ArgSpec\n    zoom: ArgSpec\n    offset: ArgSpec\n}): UvMapSource","params":[{"name":"slots","type":"{\n    center: ArgSpec\n    pan: ArgSpec\n    tilt: ArgSpec\n    fov: ArgSpec\n    zoom: ArgSpec\n    offset: ArgSpec\n}","optional":false}]},"warps.concentricRings":{"id":"warps.concentricRings","name":"concentricRings","qualifiedName":"concentricRings","category":"warps","module":"std/warps.ts","summary":"Cut the child into rings around `center` and spin each ring by its own amount.","description":"`rings` is how many rings fit in one unit of distance. `intensity` is the largest still rotation a ring can have, in degrees. `smoothness` (0–1) blends neighbouring rings instead of cutting between them. `seed` reshuffles the per-ring amounts and `speedRandomness` (0–1) lets rings spin at different rates. Declare `animatedTime: {speed: 'speed'}`.","examples":["map: warps.concentricRings({center: p('center'), intensity: p('intensity'), rings: p('rings'), smoothness: p('smoothness'), seed: p('seed'), speedRandomness: p('speedRandomness')})"],"tips":[],"see":["warps.twirl","warps.barOffset"],"import":{"statement":"import {warps} from 'shaders/std'","usage":"warps.concentricRings"},"usedBy":["ConcentricSpin"],"documented":true,"source":{"file":"std/warps.ts","line":393},"kind":"function","signature":"concentricRings(slots: {\n    center: ArgSpec\n    intensity: ArgSpec\n    rings: ArgSpec\n    smoothness: ArgSpec\n    seed: ArgSpec\n    speedRandomness: ArgSpec\n}): UvMapSource","params":[{"name":"slots","type":"{\n    center: ArgSpec\n    intensity: ArgSpec\n    rings: ArgSpec\n    smoothness: ArgSpec\n    seed: ArgSpec\n    speedRandomness: ArgSpec\n}","optional":false}]},"warps.flowNoise":{"id":"warps.flowNoise","name":"flowNoise","qualifiedName":"flowNoise","category":"warps","module":"std/warps.ts","summary":"Push the child around with a slowly flowing liquid motion.","description":"`strength` is how far pixels move, in uv (the library uses 0–0.5). `detail` scales the flow pattern (higher is finer). `seed` is a prop that picks a different pattern, even when still. Two clocks drive it: declare `animatedTime: {speed: 'speed'}` for the drift and `extraAnimatedTimes: {evolution: 'evolutionSpeed'}` for the pattern slowly reshaping.","examples":["map: warps.flowNoise({strength: p('strength'), detail: p('detail'), seed: p('seed')})"],"tips":["Speed 0 on both clocks gives a fixed distortion you can still change with `seed`."],"see":["warps.wave","warps.liquidDisplace"],"import":{"statement":"import {warps} from 'shaders/std'","usage":"warps.flowNoise"},"usedBy":["FlowField"],"documented":true,"source":{"file":"std/warps.ts","line":430},"kind":"function","signature":"flowNoise(slots: {strength: ArgSpec; detail: ArgSpec; seed: PropRef}): UvMapSource","params":[{"name":"slots","type":"{strength: ArgSpec; detail: ArgSpec; seed: PropRef}","optional":false}]},"warps.slicedSlide":{"id":"warps.slicedSlide","name":"slicedSlide","qualifiedName":"slicedSlide","category":"warps","module":"std/warps.ts","summary":"Slice the child into strips that slide out of frame in alternating directions.","description":"A wipe transition. `progress` runs 0 (whole) to 1 (gone). `angle` is the cut direction in degrees and `count` the number of strips. Set `edges: 1` (transparent) on the definition so the vacated space clears instead of smearing.","examples":["map: warps.slicedSlide({angle: p('angle'), count: p('sliceCount'), progress: p('progress')}),\nedges: 1,"],"tips":[],"see":["warps.barOffset"],"import":{"statement":"import {warps} from 'shaders/std'","usage":"warps.slicedSlide"},"usedBy":["SliceWipe"],"documented":true,"source":{"file":"std/warps.ts","line":460},"kind":"function","signature":"slicedSlide(slots: {angle: ArgSpec; count: ArgSpec; progress: ArgSpec}): UvMapSource","params":[{"name":"slots","type":"{angle: ArgSpec; count: ArgSpec; progress: ArgSpec}","optional":false}]},"warps.barOffset":{"id":"warps.barOffset","name":"barOffset","qualifiedName":"barOffset","category":"warps","module":"std/warps.ts","summary":"Cut the child into parallel bars and shift each one sideways by a random amount.","description":"A glitch or fractured look. `count` is the number of bars across the canvas, `angle` their direction in degrees, `intensity` (0–1) how far they shift. `seed` reshuffles the offsets. Bars drift with the layer's clock, so declare `animatedTime: {speed: 'speed'}`.","examples":["map: warps.barOffset({count: p('count'), angle: p('angle'), intensity: p('intensity'), seed: p('seed')})"],"tips":[],"see":["warps.slicedSlide","warps.concentricRings"],"import":{"statement":"import {warps} from 'shaders/std'","usage":"warps.barOffset"},"usedBy":["BarShift"],"documented":true,"source":{"file":"std/warps.ts","line":484},"kind":"function","signature":"barOffset(slots: {count: ArgSpec; angle: ArgSpec; intensity: ArgSpec; seed: ArgSpec}): UvMapSource","params":[{"name":"slots","type":"{count: ArgSpec; angle: ArgSpec; intensity: ArgSpec; seed: ArgSpec}","optional":false}]},"warps.gridCellDisplace":{"id":"warps.gridCellDisplace","name":"gridCellDisplace","qualifiedName":"gridCellDisplace","category":"warps","module":"std/warps.ts","summary":"Move the child by a displacement field your own simulation produces, one grid cell at a time.","description":"`output` names the simulation output (the same name you gave the field), `gridSize` the number of cells across the shorter side of the canvas. Every pixel in a cell moves together, by at most a tenth of the canvas. Add `uvRemapIdentityWhen: ({computeOutputs}) => !computeOutputs?.<output>` to the definition so the warp does nothing until the field exists.","examples":["map: warps.gridCellDisplace({gridSize: p('gridSize'), output: 'displacement'}),\nuvRemapIdentityWhen: ({computeOutputs}) => !computeOutputs?.displacement,"],"tips":[],"see":["warps.liquidDisplace"],"import":{"statement":"import {warps} from 'shaders/std'","usage":"warps.gridCellDisplace"},"usedBy":["GridDistortion"],"documented":true,"source":{"file":"std/warps.ts","line":513},"kind":"function","signature":"gridCellDisplace(slots: {gridSize: ArgSpec; output: string}): UvMapSource","params":[{"name":"slots","type":"{gridSize: ArgSpec; output: string}","optional":false}]},"warps.liquidDisplace":{"id":"warps.liquidDisplace","name":"liquidDisplace","qualifiedName":"liquidDisplace","category":"warps","module":"std/warps.ts","summary":"Move the child smoothly by a displacement field your own simulation produces.","description":"The liquid or cloth look. `output` names the simulation output. `intensity` scales the movement (the library uses 0–20), capped at about 15% of the canvas. Add `uvRemapIdentityWhen: ({computeOutputs}) => !computeOutputs?.<output>` to the definition so the warp does nothing until the field exists.","examples":["map: warps.liquidDisplace({intensity: p('intensity'), output: 'displacement'}),\nuvRemapIdentityWhen: ({computeOutputs}) => !computeOutputs?.displacement,"],"tips":[],"see":["warps.gridCellDisplace","warps.flowNoise"],"import":{"statement":"import {warps} from 'shaders/std'","usage":"warps.liquidDisplace"},"usedBy":["Liquify"],"documented":true,"source":{"file":"std/warps.ts","line":544},"kind":"function","signature":"liquidDisplace(slots: {intensity: ArgSpec; output: string}): UvMapSource","params":[{"name":"slots","type":"{intensity: ArgSpec; output: string}","optional":false}]},"warps.displaceByLayer":{"id":"warps.displaceByLayer","name":"displaceByLayer","qualifiedName":"displaceByLayer","category":"warps","module":"std/warps.ts","summary":"Distort the child using another layer's pixels as a displacement map.","description":"This one goes in `effect:`, not `map:`, because it reads a second layer. `source` is a prop marked `compileTime: true` that holds the other layer's id. `amount` (0–1) sets how far pixels move. `channels` is a prop holding `'twoAxis'` (red moves x, green moves y) or `'directional'` (brightness pushes along `angle`, in degrees). Mid-grey means no movement. `edges` is a prop marked `compileTime: true` holding `'stretch'`, `'transparent'`, `'mirror'` or `'wrap'`. With no source selected the child passes through unchanged.","examples":["effect: warps.displaceByLayer({source: p('source'), amount: p('amount'), channels: p('channelMode'), angle: p('angle'), edges: p('edges')})"],"tips":["An animated source layer displaces live."],"see":["warps.liquidDisplace"],"import":{"statement":"import {warps} from 'shaders/std'","usage":"warps.displaceByLayer"},"usedBy":["DisplacementMap"],"documented":true,"source":{"file":"std/warps.ts","line":583},"kind":"function","signature":"displaceByLayer(slots: {\n    source: PropRef\n    amount: ArgSpec\n    channels: PropRef\n    angle: ArgSpec\n    edges: PropRef\n}): GatherEffect","params":[{"name":"slots","type":"{\n    source: PropRef\n    amount: ArgSpec\n    channels: PropRef\n    angle: ArgSpec\n    edges: PropRef\n}","optional":false}]},"mask.softBand":{"id":"mask.softBand","name":"softBand","qualifiedName":"softBand","category":"mask","module":"std/mask.ts","summary":"A soft band around a line, sharp on one side and long on the other.","description":"`distance` is the signed distance from the line: negative below it, positive above. Coverage rises from 0 to 1 as the distance crosses `below.from` to `below.to`, then falls back to 0 as it crosses `above.from` (default 0) to `above.to`. The shape under curtains, horizons and underlines.","examples":["const curtain = mask.softBand({distance: sub(uv.member('y'), pathY), below: {from: -0.04, to: 0.015}, above: {to: height}})"],"tips":[],"see":["mask.softDisc","mask.crispStroke"],"import":{"statement":"import {mask} from 'shaders/std'","usage":"mask.softBand"},"usedBy":["Aurora"],"documented":true,"source":{"file":"std/mask.ts","line":35},"kind":"function","signature":"softBand(opts: {\n    distance: Expr\n    below: {from: Expr | number; to: Expr | number}\n    above: {from?: Expr | number; to: Expr | number}\n}): Expr","params":[{"name":"opts","type":"{\n    distance: Expr\n    below: {from: Expr | number; to: Expr | number}\n    above: {from?: Expr | number; to: Expr | number}\n}","optional":false}]},"mask.softDisc":{"id":"mask.softDisc","name":"softDisc","qualifiedName":"softDisc","category":"mask","module":"std/mask.ts","summary":"A filled disc with a soft, shaped edge.","description":"`dist` is the distance from the disc's center and `radius` its size, both in the same units. The edge fades over `softness.width` either side of the radius, and `softness.curve` bends that fade: 1 is even, higher values pull it tighter toward the rim. The look of blobs, orbs and glow dots.","examples":["const orb = mask.softDisc({dist, radius: params.uniforms.size, softness: {width: 0.08, curve: 1.5}})"],"tips":[],"see":["mask.crispFill","mask.softBand"],"import":{"statement":"import {mask} from 'shaders/std'","usage":"mask.softDisc"},"usedBy":["Blob"],"documented":true,"source":{"file":"std/mask.ts","line":60},"kind":"function","signature":"softDisc(opts: {\n    dist: Expr\n    radius: Expr | number\n    softness: {width: Expr | number; curve: Expr | number}\n}): Expr","params":[{"name":"opts","type":"{\n    dist: Expr\n    radius: Expr | number\n    softness: {width: Expr | number; curve: Expr | number}\n}","optional":false}]},"mask.crispFill":{"id":"mask.crispFill","name":"crispFill","qualifiedName":"crispFill","category":"mask","module":"std/mask.ts","summary":"A clean fill of a shape: 1 inside, 0 outside.","description":"`distance` is a signed distance, negative inside the shape, such as one from the `shape` words. `footprint` is how wide the edge blends. One pixel in the distance's units gives an anti-aliased edge. A wider value gives a deliberate soft edge.","examples":["const bar = mask.crispFill({distance: shape.roundedRect(x, y, w, h, rounding), footprint: px})"],"tips":["One pixel in uv is `div(1, viewport.member('y'))`, with `viewport` from `frames.surfaceOf`."],"see":["mask.crispStroke","mask.softDisc"],"import":{"statement":"import {mask} from 'shaders/std'","usage":"mask.crispFill"},"usedBy":["Waveform"],"documented":true,"source":{"file":"std/mask.ts","line":86},"kind":"function","signature":"crispFill(opts: {distance: Expr; footprint: Expr | number}): Expr","params":[{"name":"opts","type":"{distance: Expr; footprint: Expr | number}","optional":false}]},"mask.crispStroke":{"id":"mask.crispStroke","name":"crispStroke","qualifiedName":"crispStroke","category":"mask","module":"std/mask.ts","summary":"A clean outline of a shape, or a drawn curve, of a given width.","description":"`distance` is a signed distance to the shape's edge or to the curve. The stroke is centered on the edge and `width` wide, in the distance's units. `footprint` blends the stroke's own edges, as in `crispFill`.","examples":["const outline = mask.crispStroke({distance: shape.circle(x, y, radius), width: params.uniforms.lineWidth, footprint: px})"],"tips":[],"see":["mask.crispFill"],"import":{"statement":"import {mask} from 'shaders/std'","usage":"mask.crispStroke"},"usedBy":["Waveform"],"documented":true,"source":{"file":"std/mask.ts","line":104},"kind":"function","signature":"crispStroke(opts: {distance: Expr; width: Expr | number; footprint: Expr | number}): Expr","params":[{"name":"opts","type":"{distance: Expr; width: Expr | number; footprint: Expr | number}","optional":false}]},"mask.radialMask":{"id":"mask.radialMask","name":"radialMask","qualifiedName":"radialMask","category":"mask","module":"std/mask.ts","summary":"An amount that is 0 near a center and grows to 1 with distance, for an effect's strength.","description":"Coverage stays 0 inside `radius` and reaches 1 at `radius + falloff`, measured in uv with the aspect corrected so the ring is round. Every slot takes a prop: `center` is a position prop, `radius` and `falloff` are numbers in uv. Chain `.times(p('intensity'))` to scale it.","examples":["effect: tintToward(p('color'), {amount: radialMask({center: p('center'), radius: p('radius'), falloff: p('falloff')}).times(p('intensity'))})"],"tips":["Also exported at the top level of `shaders/std`, so `radialMask` works without the `mask.` prefix."],"see":["mask.softDisc"],"import":{"statement":"import {mask} from 'shaders/std'","usage":"mask.radialMask"},"usedBy":["Vignette"],"documented":true,"source":{"file":"std/mask.ts","line":124},"kind":"function","signature":"radialMask(slots: {center: PropRef; radius: PropRef; falloff: PropRef}): Scalar","params":[{"name":"slots","type":"{center: PropRef; radius: PropRef; falloff: PropRef}","optional":false}]},"math.add":{"id":"math.add","name":"add","qualifiedName":"add","category":"math","module":"std/math.ts","summary":"The sum of two values.","description":"Works on numbers and on vectors of the same size.","examples":["const lifted = add(mul(noise, 0.5), 0.5) // a −1..1 signal moved into 0..1"],"tips":[],"see":["math.sub","math.mul"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.add"},"usedBy":["Aurora","Blob","BrushedMetal","CarbonFiber","ChromaFlow","Chrome","CompressionArtifacts","Crystal","FlowingGradient","Frost","Goo","Heatmap","Hologram","Holographic","Irradiance","LiquidMetal","Marble","MeshGradient","Nebula","Obsidian","Plastic","Prism","ReactionDiffusion","Satin","Sparkle","StudioBackground","Voxels","Water","Waveform"],"documented":true,"source":{"file":"std/math.ts","line":46},"kind":"function","signature":"add(a: Val, b: Val): Expr","params":[{"name":"a","type":"Val","optional":false},{"name":"b","type":"Val","optional":false}]},"math.sub":{"id":"math.sub","name":"sub","qualifiedName":"sub","category":"math","module":"std/math.ts","summary":"The first value minus the second.","description":"","examples":["const outside = sub(1, coverage) // flip a 0..1 mask"],"tips":[],"see":["math.add","math.neg"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.sub"},"usedBy":["Aurora","BrushedMetal","CarbonFiber","Chrome","Crystal","FlowingGradient","Frost","Goo","Heatmap","Hologram","Holographic","Irradiance","LiquidMetal","MeshGradient","Nebula","Obsidian","PixelThrow","Plastic","Prism","ReactionDiffusion","Satin","StudioBackground","Voxels","Water","Waveform"],"documented":true,"source":{"file":"std/math.ts","line":56},"kind":"function","signature":"sub(a: Val, b: Val): Expr","params":[{"name":"a","type":"Val","optional":false},{"name":"b","type":"Val","optional":false}]},"math.mul":{"id":"math.mul","name":"mul","qualifiedName":"mul","category":"math","module":"std/math.ts","summary":"The product of two values.","description":"Multiply a color by a 0..1 mask to fade it, or a coordinate by a prop to scale it.","examples":["const rgb = mul(params.uniforms.color.member('rgb'), coverage)"],"tips":[],"see":["math.div","math.add"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.mul"},"usedBy":["Arc","Aurora","Blob","BrushedMetal","CarbonFiber","Chrome","Circle","CompressionArtifacts","Crystal","FlowingGradient","Frost","Goo","Heatmap","Hologram","Holographic","InkFlow","Irradiance","LiquidMetal","Marble","MeshGradient","Nebula","Obsidian","PixelThrow","Plastic","Prism","ReactionDiffusion","Satin","SmokeFill","Sparkle","StudioBackground","Surface3D","TimeTrail","Voxels","Water","Waveform"],"documented":true,"source":{"file":"std/math.ts","line":68},"kind":"function","signature":"mul(a: Val, b: Val): Expr","params":[{"name":"a","type":"Val","optional":false},{"name":"b","type":"Val","optional":false}]},"math.div":{"id":"math.div","name":"div","qualifiedName":"div","category":"math","module":"std/math.ts","summary":"The first value divided by the second.","description":"Dividing by zero produces infinity on the GPU, so guard a prop with `max` first.","examples":["const normalized = div(dist, max(params.uniforms.radius, 0.001))"],"tips":[],"see":["math.mul","math.max"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.div"},"usedBy":["Aurora","CarbonFiber","Chrome","CompressionArtifacts","Crystal","FlowingGradient","Frost","Goo","Heatmap","Hologram","Holographic","Irradiance","Marble","MeshGradient","Plastic","Prism","ReactionDiffusion","Satin","Sparkle","StudioBackground","TimeTrail","Water","Waveform"],"documented":true,"source":{"file":"std/math.ts","line":80},"kind":"function","signature":"div(a: Val, b: Val): Expr","params":[{"name":"a","type":"Val","optional":false},{"name":"b","type":"Val","optional":false}]},"math.neg":{"id":"math.neg","name":"neg","qualifiedName":"neg","category":"math","module":"std/math.ts","summary":"The value with its sign flipped.","description":"","examples":["const falloff = exp(neg(mul(dist, 4)))"],"tips":[],"see":["math.sub","math.abs"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.neg"},"usedBy":["Prism","ReactionDiffusion","Satin","StudioBackground","Voxels","Waveform"],"documented":true,"source":{"file":"std/math.ts","line":90},"kind":"function","signature":"neg(a: Val): Expr","params":[{"name":"a","type":"Val","optional":false}]},"math.abs":{"id":"math.abs","name":"abs","qualifiedName":"abs","category":"math","module":"std/math.ts","summary":"The value without its sign.","description":"Turns a signed distance into a distance from the line, so a stroke covers both sides.","examples":["const fromLine = abs(sub(uv.member('y'), 0.5))"],"tips":[],"see":["math.sign","math.neg"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.abs"},"usedBy":["CarbonFiber","Chrome","Crystal","FlowingGradient","Heatmap","Hologram","Nebula","Plastic","Satin","Water","Waveform"],"documented":true,"source":{"file":"std/math.ts","line":105},"kind":"function","signature":"abs(x: Val): Expr","params":[{"name":"x","type":"Val","optional":false}]},"math.floor":{"id":"math.floor","name":"floor","qualifiedName":"floor","category":"math","module":"std/math.ts","summary":"The value rounded down to a whole number.","description":"Gives the cell index when you tile a coordinate.","examples":["const column = floor(mul(uv.member('x'), params.uniforms.columns))"],"tips":[],"see":["math.fract","math.ceil"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.floor"},"usedBy":["CarbonFiber","CompressionArtifacts","Crystal","Waveform"],"documented":true,"source":{"file":"std/math.ts","line":117},"kind":"function","signature":"floor(x: Val): Expr","params":[{"name":"x","type":"Val","optional":false}]},"math.ceil":{"id":"math.ceil","name":"ceil","qualifiedName":"ceil","category":"math","module":"std/math.ts","summary":"The value rounded up to a whole number.","description":"","examples":["const rows = ceil(div(height, cell))"],"tips":[],"see":["math.floor"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.ceil"},"usedBy":[],"documented":true,"source":{"file":"std/math.ts","line":127},"kind":"function","signature":"ceil(x: Val): Expr","params":[{"name":"x","type":"Val","optional":false}]},"math.fract":{"id":"math.fract","name":"fract","qualifiedName":"fract","category":"math","module":"std/math.ts","summary":"The fractional part of a value, 0..1.","description":"Repeats a coordinate every whole unit, which is how a pattern tiles.","examples":["const inCell = fract(mul(uv, params.uniforms.tiles)) // 0..1 inside each tile"],"tips":[],"see":["math.floor"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.fract"},"usedBy":["CarbonFiber","Crystal","Heatmap","Hologram","Waveform"],"documented":true,"source":{"file":"std/math.ts","line":139},"kind":"function","signature":"fract(x: Val): Expr","params":[{"name":"x","type":"Val","optional":false}]},"math.sqrt":{"id":"math.sqrt","name":"sqrt","qualifiedName":"sqrt","category":"math","module":"std/math.ts","summary":"The square root of a value.","description":"","examples":["const dist = sqrt(add(mul(dx, dx), mul(dy, dy)))"],"tips":[],"see":["math.length","math.pow"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.sqrt"},"usedBy":["Chrome","Crystal","Frost","Goo","Plastic","Sparkle","Waveform"],"documented":true,"source":{"file":"std/math.ts","line":149},"kind":"function","signature":"sqrt(x: Val): Expr","params":[{"name":"x","type":"Val","optional":false}]},"math.pow":{"id":"math.pow","name":"pow","qualifiedName":"pow","category":"math","module":"std/math.ts","summary":"The first value raised to the power of the second.","description":"Shapes a 0..1 value: powers above 1 push it toward 0, powers below 1 toward 1. Keep the base non-negative.","examples":["const hardened = pow(coverage, params.uniforms.hardness)"],"tips":[],"see":["math.sqrt","math.exp"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.pow"},"usedBy":["Aurora","CarbonFiber","Crystal","FlowingGradient","Frost","Goo","Heatmap","Hologram","Holographic","Irradiance","Nebula","Plastic","ReactionDiffusion","Voxels","Water"],"documented":true,"source":{"file":"std/math.ts","line":162},"kind":"function","signature":"pow(x: Val, y: Val): Expr","params":[{"name":"x","type":"Val","optional":false},{"name":"y","type":"Val","optional":false}]},"math.exp":{"id":"math.exp","name":"exp","qualifiedName":"exp","category":"math","module":"std/math.ts","summary":"e raised to the value.","description":"With a negative argument it is the classic smooth falloff.","examples":["const glow = exp(neg(mul(dist, params.uniforms.falloff)))"],"tips":[],"see":["math.exp2","math.log","math.gaussBell"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.exp"},"usedBy":["Aurora","Chrome","Goo","Heatmap","Hologram","MeshGradient","Prism","StudioBackground"],"documented":true,"source":{"file":"std/math.ts","line":174},"kind":"function","signature":"exp(x: Val): Expr","params":[{"name":"x","type":"Val","optional":false}]},"math.log":{"id":"math.log","name":"log","qualifiedName":"log","category":"math","module":"std/math.ts","summary":"The natural logarithm of a value.","description":"","examples":["const octave = log(max(scale, 1))"],"tips":[],"see":["math.exp"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.log"},"usedBy":[],"documented":true,"source":{"file":"std/math.ts","line":184},"kind":"function","signature":"log(x: Val): Expr","params":[{"name":"x","type":"Val","optional":false}]},"math.sin":{"id":"math.sin","name":"sin","qualifiedName":"sin","category":"math","module":"std/math.ts","summary":"The sine of an angle in radians, −1..1.","description":"","examples":["const wave = sin(add(mul(uv.member('x'), 12), time))"],"tips":["A degrees prop needs `mul(angle, 0.017453292519943295)` first."],"see":["math.cos","math.tan"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.sin"},"usedBy":["BrushedMetal","CarbonFiber","Chrome","Crystal","FlowingGradient","Frost","Goo","Hologram","Holographic","LiquidMetal","Marble","MeshGradient","ReactionDiffusion","Voxels","Water"],"documented":true,"source":{"file":"std/math.ts","line":195},"kind":"function","signature":"sin(x: Val): Expr","params":[{"name":"x","type":"Val","optional":false}]},"math.cos":{"id":"math.cos","name":"cos","qualifiedName":"cos","category":"math","module":"std/math.ts","summary":"The cosine of an angle in radians, −1..1.","description":"","examples":["const spun = rotate2(delta, cos(angle), sin(angle))"],"tips":["A degrees prop needs `mul(angle, 0.017453292519943295)` first."],"see":["math.sin","math.rotate2"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.cos"},"usedBy":["Chrome","Crystal","FlowingGradient","Goo","MeshGradient","ReactionDiffusion","Voxels"],"documented":true,"source":{"file":"std/math.ts","line":206},"kind":"function","signature":"cos(x: Val): Expr","params":[{"name":"x","type":"Val","optional":false}]},"math.tan":{"id":"math.tan","name":"tan","qualifiedName":"tan","category":"math","module":"std/math.ts","summary":"The tangent of an angle in radians.","description":"","examples":["const slope = tan(mul(params.uniforms.tilt, 0.017453292519943295))"],"tips":[],"see":["math.sin","math.cos","math.atan2"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.tan"},"usedBy":[],"documented":true,"source":{"file":"std/math.ts","line":216},"kind":"function","signature":"tan(x: Val): Expr","params":[{"name":"x","type":"Val","optional":false}]},"math.atan2":{"id":"math.atan2","name":"atan2","qualifiedName":"atan2","category":"math","module":"std/math.ts","summary":"The angle of the point (x, y) in radians, −π..π.","description":"Takes y first. Turns an offset from a center into an angle around it.","examples":["const angle = atan2(delta.member('y'), delta.member('x'))"],"tips":[],"see":["math.sin","math.cos"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.atan2"},"usedBy":[],"documented":true,"source":{"file":"std/math.ts","line":228},"kind":"function","signature":"atan2(y: Val, x: Val): Expr","params":[{"name":"y","type":"Val","optional":false},{"name":"x","type":"Val","optional":false}]},"math.min":{"id":"math.min","name":"min","qualifiedName":"min","category":"math","module":"std/math.ts","summary":"The smaller of two values.","description":"","examples":["const capped = min(brightness, 1)"],"tips":[],"see":["math.max","math.clamp"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.min"},"usedBy":["CarbonFiber","Chrome","Goo","Heatmap","Hologram","ReactionDiffusion","Sparkle"],"documented":true,"source":{"file":"std/math.ts","line":238},"kind":"function","signature":"min(a: Val, b: Val): Expr","params":[{"name":"a","type":"Val","optional":false},{"name":"b","type":"Val","optional":false}]},"math.max":{"id":"math.max","name":"max","qualifiedName":"max","category":"math","module":"std/math.ts","summary":"The larger of two values.","description":"","examples":["const safeRadius = max(params.uniforms.radius, 0.001)"],"tips":[],"see":["math.min","math.clamp"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.max"},"usedBy":["Aurora","Chrome","Crystal","FlowingGradient","Frost","Goo","Heatmap","Hologram","InkFlow","Irradiance","Marble","MeshGradient","Plastic","Prism","ReactionDiffusion","Sparkle","StudioBackground","TimeTrail","Voxels","Waveform"],"documented":true,"source":{"file":"std/math.ts","line":248},"kind":"function","signature":"max(a: Val, b: Val): Expr","params":[{"name":"a","type":"Val","optional":false},{"name":"b","type":"Val","optional":false}]},"math.clamp":{"id":"math.clamp","name":"clamp","qualifiedName":"clamp","category":"math","module":"std/math.ts","summary":"The value limited to the range `lo`..`hi`.","description":"","examples":["const alpha = clamp(add(body, ring), 0, 1)"],"tips":[],"see":["math.min","math.max"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.clamp"},"usedBy":["Aurora","BrushedMetal","CarbonFiber","Chrome","CompressionArtifacts","Crystal","FlowingGradient","Frost","Goo","Heatmap","Hologram","Holographic","InkFlow","LiquidMetal","MeshGradient","Nebula","PixelThrow","Plastic","Prism","ReactionDiffusion","Satin","SmokeFill","StudioBackground","Surface3D","Voxels","Water","Waveform"],"documented":true,"source":{"file":"std/math.ts","line":258},"kind":"function","signature":"clamp(x: Val, lo: Val, hi: Val): Expr","params":[{"name":"x","type":"Val","optional":false},{"name":"lo","type":"Val","optional":false},{"name":"hi","type":"Val","optional":false}]},"math.mix":{"id":"math.mix","name":"mix","qualifiedName":"mix","category":"math","module":"std/math.ts","summary":"A blend from `a` to `b` by `t`, where 0 gives `a` and 1 gives `b`.","description":"Works on numbers and on colors. For two colors, pass `t` through `splat3` to blend every channel by the same amount.","examples":["const rgb = mix(params.uniforms.colorA.member('rgb'), params.uniforms.colorB.member('rgb'), splat3(coverage))"],"tips":[],"see":["math.smoothstep","math.splat3"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.mix"},"usedBy":["Aurora","BrushedMetal","CarbonFiber","Chrome","Crystal","FlowingGradient","Frost","Goo","Heatmap","Hologram","Holographic","Irradiance","LiquidMetal","Nebula","Obsidian","PixelThrow","Plastic","Polygon","Prism","ReactionDiffusion","Satin","Sparkle","StudioBackground","Voxels","Water","Waveform"],"documented":true,"source":{"file":"std/math.ts","line":271},"kind":"function","signature":"mix(a: Val, b: Val, t: Val): Expr","params":[{"name":"a","type":"Val","optional":false},{"name":"b","type":"Val","optional":false},{"name":"t","type":"Val","optional":false}]},"math.step":{"id":"math.step","name":"step","qualifiedName":"step","category":"math","module":"std/math.ts","summary":"A hard edge: 0 when `x` is below `edge`, 1 from `edge` on.","description":"","examples":["const lit = step(params.uniforms.threshold, brightness)"],"tips":[],"see":["math.smoothstep"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.step"},"usedBy":["Sparkle","Waveform"],"documented":true,"source":{"file":"std/math.ts","line":281},"kind":"function","signature":"step(edge: Val, x: Val): Expr","params":[{"name":"edge","type":"Val","optional":false},{"name":"x","type":"Val","optional":false}]},"math.smoothstep":{"id":"math.smoothstep","name":"smoothstep","qualifiedName":"smoothstep","category":"math","module":"std/math.ts","summary":"A soft edge: 0 below `e0`, 1 above `e1`, an S-curve between.","description":"The everyday word for turning a distance into a soft mask. Swap `e0` and `e1` to fade the other way.","examples":["const coverage = sub(1, smoothstep(radius, add(radius, softness), dist)) // 1 inside, fading out"],"tips":[],"see":["math.step","math.mix"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.smoothstep"},"usedBy":["Aurora","Blob","CarbonFiber","Chrome","Crystal","FlowingGradient","Frost","Goo","Heatmap","Hologram","Holographic","Irradiance","LiquidMetal","Marble","MeshGradient","Nebula","Obsidian","Plastic","Prism","ReactionDiffusion","Sparkle","StudioBackground","Voxels","Water","Waveform"],"documented":true,"source":{"file":"std/math.ts","line":294},"kind":"function","signature":"smoothstep(e0: Val, e1: Val, x: Val): Expr","params":[{"name":"e0","type":"Val","optional":false},{"name":"e1","type":"Val","optional":false},{"name":"x","type":"Val","optional":false}]},"math.sign":{"id":"math.sign","name":"sign","qualifiedName":"sign","category":"math","module":"std/math.ts","summary":"−1, 0 or 1 by the sign of the value.","description":"","examples":["const side = sign(sub(uv.member('x'), 0.5))"],"tips":[],"see":["math.abs"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.sign"},"usedBy":[],"documented":true,"source":{"file":"std/math.ts","line":304},"kind":"function","signature":"sign(x: Val): Expr","params":[{"name":"x","type":"Val","optional":false}]},"math.length":{"id":"math.length","name":"length","qualifiedName":"length","category":"math","module":"std/math.ts","summary":"The length of a vector.","description":"The distance from the origin, or from a center when you pass the offset from it.","examples":["const dist = length(sub(uv, params.uniforms.center))"],"tips":[],"see":["math.distance","math.normalize"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.length"},"usedBy":["Chrome","LiquidMetal","MeshGradient","Water"],"documented":true,"source":{"file":"std/math.ts","line":316},"kind":"function","signature":"length(v: Val): Expr","params":[{"name":"v","type":"Val","optional":false}]},"math.distance":{"id":"math.distance","name":"distance","qualifiedName":"distance","category":"math","module":"std/math.ts","summary":"The distance between two points.","description":"","examples":["const toPointer = distance(uv, params.ctx.pointer)"],"tips":[],"see":["math.length"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.distance"},"usedBy":[],"documented":true,"source":{"file":"std/math.ts","line":326},"kind":"function","signature":"distance(a: Val, b: Val): Expr","params":[{"name":"a","type":"Val","optional":false},{"name":"b","type":"Val","optional":false}]},"math.dot":{"id":"math.dot","name":"dot","qualifiedName":"dot","category":"math","module":"std/math.ts","summary":"The dot product of two vectors.","description":"With two unit vectors it is the cosine of the angle between them, which is how a light direction meets a surface normal.","examples":["const facing = max(dot(normal, lightDir), 0)"],"tips":[],"see":["math.normalize","math.cross"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.dot"},"usedBy":["CarbonFiber","Crystal","Frost","Goo","Heatmap","Holographic","Irradiance","Nebula","PixelThrow","Plastic","ReactionDiffusion","Sparkle","TimeTrail","Voxels","Water"],"documented":true,"source":{"file":"std/math.ts","line":339},"kind":"function","signature":"dot(a: Val, b: Val): Expr","params":[{"name":"a","type":"Val","optional":false},{"name":"b","type":"Val","optional":false}]},"math.normalize":{"id":"math.normalize","name":"normalize","qualifiedName":"normalize","category":"math","module":"std/math.ts","summary":"The vector scaled to length 1.","description":"A zero vector gives NaN, so keep the input non-zero.","examples":["const dir = normalize(sub(params.uniforms.target, params.uniforms.origin))"],"tips":[],"see":["math.length","math.dot"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.normalize"},"usedBy":["Crystal","Frost","Goo","Hologram","Holographic","Irradiance","Plastic","ReactionDiffusion","Voxels","Water"],"documented":true,"source":{"file":"std/math.ts","line":351},"kind":"function","signature":"normalize(v: Val): Expr","params":[{"name":"v","type":"Val","optional":false}]},"math.cross":{"id":"math.cross","name":"cross","qualifiedName":"cross","category":"math","module":"std/math.ts","summary":"The cross product of two 3D vectors.","description":"","examples":["const normal = normalize(cross(tangentX, tangentY))"],"tips":[],"see":["math.dot","math.normalize"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.cross"},"usedBy":[],"documented":true,"source":{"file":"std/math.ts","line":361},"kind":"function","signature":"cross(a: Val, b: Val): Expr","params":[{"name":"a","type":"Val","optional":false},{"name":"b","type":"Val","optional":false}]},"math.rotate2":{"id":"math.rotate2","name":"rotate2","qualifiedName":"rotate2","category":"math","module":"std/math.ts","summary":"A 2D point rotated around the origin.","description":"Pass the cosine and sine of the angle in radians, computed once, and the same pair rotates every point.","examples":["const spun = local(rotate2(delta, cos(angle), sin(angle)), 'spun')"],"tips":[],"see":["math.cos","math.sin"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.rotate2"},"usedBy":["FlowingGradient","MeshGradient"],"documented":true,"source":{"file":"std/math.ts","line":374},"kind":"function","signature":"rotate2(p: Val, cosA: Val, sinA: Val): Expr","params":[{"name":"p","type":"Val","optional":false},{"name":"cosA","type":"Val","optional":false},{"name":"sinA","type":"Val","optional":false}]},"math.reflect":{"id":"math.reflect","name":"reflect","qualifiedName":"reflect","category":"math","module":"std/math.ts","summary":"The direction `i` bounced off a surface with unit normal `n`.","description":"","examples":["const bounce = reflect(viewDir, normal)"],"tips":[],"see":["math.refract","math.normalize"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.reflect"},"usedBy":[],"documented":true,"source":{"file":"std/math.ts","line":388},"kind":"function","signature":"reflect(i: Val, n: Val): Expr","params":[{"name":"i","type":"Val","optional":false},{"name":"n","type":"Val","optional":false}]},"math.refract":{"id":"math.refract","name":"refract","qualifiedName":"refract","category":"math","module":"std/math.ts","summary":"The direction `i` bent through a surface with unit normal `n`.","description":"`eta` is the ratio of refractive indices, about 0.66 going from air into glass.","examples":["const bent = refract(viewDir, normal, 0.66)"],"tips":[],"see":["math.reflect"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.refract"},"usedBy":[],"documented":true,"source":{"file":"std/math.ts","line":400},"kind":"function","signature":"refract(i: Val, n: Val, eta: Val): Expr","params":[{"name":"i","type":"Val","optional":false},{"name":"n","type":"Val","optional":false},{"name":"eta","type":"Val","optional":false}]},"math.exp2":{"id":"math.exp2","name":"exp2","qualifiedName":"exp2","category":"math","module":"std/math.ts","summary":"2 raised to the value.","description":"","examples":["const octaveScale = exp2(octave)"],"tips":[],"see":["math.exp"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.exp2"},"usedBy":["Voxels"],"documented":true,"source":{"file":"std/math.ts","line":410},"kind":"function","signature":"exp2(x: Val): Expr","params":[{"name":"x","type":"Val","optional":false}]},"math.hashPhase":{"id":"math.hashPhase","name":"hashPhase","qualifiedName":"hashPhase","category":"math","module":"std/math.ts","summary":"A random but stable angle in radians, 0..2π, from a seed.","description":"Use it to start each of several sine motions at a different point so they do not move in step. `salt` is a pair of constants. Give each independent phase its own pair.","examples":["const wobble = sin(add(mul(t, 0.7), hashPhase(params.uniforms.seed, {k: 53.7, big: 3847.2})))"],"tips":[],"see":["math.sin"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.hashPhase"},"usedBy":["Aurora"],"documented":true,"source":{"file":"std/math.ts","line":424},"kind":"function","signature":"hashPhase(seed: Val, salt: {k: number; big: number}): Expr","params":[{"name":"seed","type":"Val","optional":false},{"name":"salt","type":"{k: number; big: number}","optional":false}]},"math.lt":{"id":"math.lt","name":"lt","qualifiedName":"lt","category":"math","module":"std/math.ts","summary":"True when `a` is less than `b`.","description":"A condition for `select`.","examples":["const rgb = select(lt(dist, radius), insideRgb, outsideRgb)"],"tips":[],"see":["math.le","math.gt","math.ge","math.select"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.lt"},"usedBy":[],"documented":true,"source":{"file":"std/math.ts","line":443},"kind":"function","signature":"lt(a: Val, b: Val): Expr","params":[{"name":"a","type":"Val","optional":false},{"name":"b","type":"Val","optional":false}]},"math.le":{"id":"math.le","name":"le","qualifiedName":"le","category":"math","module":"std/math.ts","summary":"True when `a` is less than or equal to `b`.","description":"","examples":["const inBand = select(le(abs(d), width), 1, 0)"],"tips":[],"see":["math.lt","math.gt","math.ge","math.select"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.le"},"usedBy":["Goo"],"documented":true,"source":{"file":"std/math.ts","line":453},"kind":"function","signature":"le(a: Val, b: Val): Expr","params":[{"name":"a","type":"Val","optional":false},{"name":"b","type":"Val","optional":false}]},"math.gt":{"id":"math.gt","name":"gt","qualifiedName":"gt","category":"math","module":"std/math.ts","summary":"True when `a` is greater than `b`.","description":"","examples":["const highlights = select(gt(luma, 0.5), luma, 0)"],"tips":[],"see":["math.ge","math.lt","math.le","math.select"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.gt"},"usedBy":["Crystal","Frost","Irradiance","ReactionDiffusion"],"documented":true,"source":{"file":"std/math.ts","line":463},"kind":"function","signature":"gt(a: Val, b: Val): Expr","params":[{"name":"a","type":"Val","optional":false},{"name":"b","type":"Val","optional":false}]},"math.ge":{"id":"math.ge","name":"ge","qualifiedName":"ge","category":"math","module":"std/math.ts","summary":"True when `a` is greater than or equal to `b`.","description":"","examples":["const rightHalf = select(ge(uv.member('x'), 0.5), 1, 0)"],"tips":[],"see":["math.gt","math.lt","math.le","math.select"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.ge"},"usedBy":["CarbonFiber"],"documented":true,"source":{"file":"std/math.ts","line":473},"kind":"function","signature":"ge(a: Val, b: Val): Expr","params":[{"name":"a","type":"Val","optional":false},{"name":"b","type":"Val","optional":false}]},"math.select":{"id":"math.select","name":"select","qualifiedName":"select","category":"math","module":"std/math.ts","summary":"`ifTrue` when the condition holds, otherwise `ifFalse`.","description":"Both values are computed at every pixel, so this picks rather than skips work. For a soft transition use `mix` or `smoothstep` instead.","examples":["const rgb = select(lt(dist, radius), params.uniforms.fill.member('rgb'), params.uniforms.background.member('rgb'))"],"tips":[],"see":["math.lt","math.gt","math.mix"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.select"},"usedBy":["CarbonFiber","Crystal","ReactionDiffusion"],"documented":true,"source":{"file":"std/math.ts","line":486},"kind":"function","signature":"select(cond: Expr, ifTrue: Val, ifFalse: Val): Expr","params":[{"name":"cond","type":"Expr","optional":false},{"name":"ifTrue","type":"Val","optional":false},{"name":"ifFalse","type":"Val","optional":false}]},"math.float":{"id":"math.float","name":"float","qualifiedName":"float","category":"math","module":"std/math.ts","summary":"A plain number as an expression.","description":"Rarely needed: every word here accepts numbers directly. Reach for it when something wants an expression and you only have a constant.","examples":["const half = float(0.5)"],"tips":[],"see":["math.vec2","math.vec3","math.vec4"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.float"},"usedBy":["CarbonFiber","Chrome","Crystal","Frost","Goo","Heatmap","Hologram","Holographic","Irradiance","Nebula","StudioBackground","Water"],"documented":true,"source":{"file":"std/math.ts","line":504},"kind":"function","signature":"float(n: number): Expr","params":[{"name":"n","type":"number","optional":false}]},"math.vec2":{"id":"math.vec2","name":"vec2","qualifiedName":"vec2","category":"math","module":"std/math.ts","summary":"A 2D vector from two values.","description":"","examples":["const offset = vec2(params.uniforms.shiftX, params.uniforms.shiftY)"],"tips":[],"see":["math.vec3","math.vec4"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.vec2"},"usedBy":["ChromaFlow","Chrome","CompressionArtifacts","Crystal","FlowingGradient","Frost","Heatmap","Hologram","Irradiance","LiquidMetal","Marble","MeshGradient","Nebula","PixelSort","Plastic","ReactionDiffusion","Satin","Sparkle","Water"],"documented":true,"source":{"file":"std/math.ts","line":514},"kind":"function","signature":"vec2(x: Val, y: Val): Expr","params":[{"name":"x","type":"Val","optional":false},{"name":"y","type":"Val","optional":false}]},"math.vec3":{"id":"math.vec3","name":"vec3","qualifiedName":"vec3","category":"math","module":"std/math.ts","summary":"A 3D vector from three values, often a color without alpha.","description":"","examples":["const warmWhite = vec3(1, 0.96, 0.9)"],"tips":[],"see":["math.vec2","math.vec4","math.splat3"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.vec3"},"usedBy":["Aurora","Blob","Chrome","Crystal","FlowingGradient","Frost","Goo","Heatmap","Hologram","Holographic","Irradiance","MeshGradient","Nebula","PixelThrow","Plastic","ReactionDiffusion","Satin","Sparkle","StudioBackground","Surface3D","TimeTrail","Voxels","Water"],"documented":true,"source":{"file":"std/math.ts","line":524},"kind":"function","signature":"vec3(x: Val, y: Val, z: Val): Expr","params":[{"name":"x","type":"Val","optional":false},{"name":"y","type":"Val","optional":false},{"name":"z","type":"Val","optional":false}]},"math.vec4":{"id":"math.vec4","name":"vec4","qualifiedName":"vec4","category":"math","module":"std/math.ts","summary":"A 4D vector, usually the final color with alpha.","description":"Takes four numbers, or a vec3 followed by alpha. Return one of these from `paint:`.","examples":["return vec4(rgb, coverage)"],"tips":[],"see":["math.vec3","math.vec2"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.vec4"},"usedBy":["Aurora","Blob","BrushedMetal","CarbonFiber","Chrome","Crystal","FlowingGradient","Frost","Goo","Heatmap","Hologram","Holographic","Irradiance","LiquidMetal","Nebula","Obsidian","Plastic","Prism","ReactionDiffusion","Satin","Sparkle","StudioBackground","Surface3D","TimeTrail","Voxels","Water","Waveform"],"documented":true,"source":{"file":"std/math.ts","line":536},"kind":"function","signature":"vec4(...parts: Val[]): Expr","params":[{"name":"...parts","type":"Val[]","optional":true}]},"math.splat3":{"id":"math.splat3","name":"splat3","qualifiedName":"splat3","category":"math","module":"std/math.ts","summary":"One number copied into all three channels of a vec3.","description":"The usual way to blend two colors by a single amount with `mix`.","examples":["const rgb = mix(darkRgb, lightRgb, splat3(brightness))"],"tips":[],"see":["math.mix","math.vec3"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.splat3"},"usedBy":["Aurora","CarbonFiber","Chrome","Crystal","FlowingGradient","Frost","Hologram","Holographic","Irradiance","Nebula","Obsidian","Plastic","Prism","ReactionDiffusion","Sparkle","StudioBackground","Water"],"documented":true,"source":{"file":"std/math.ts","line":548},"kind":"function","signature":"splat3(x: Val): Expr","params":[{"name":"x","type":"Val","optional":false}]},"math.local":{"id":"math.local","name":"local","qualifiedName":"local","category":"math","module":"std/math.ts","summary":"A value computed once per pixel and shared by everything that reads it.","description":"Wrap any expression you use twice or more. `hint` is a short name that appears in the generated code and only has to be readable.","examples":["const dist = local(length(sub(uv, params.uniforms.center)), 'dist')"],"tips":["Without it, the same math is repeated at every place you use the value."],"see":[],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.local"},"usedBy":["Aurora","Blob","BrushedMetal","CarbonFiber","Chrome","CompressionArtifacts","Crystal","FlowingGradient","Frost","Goo","Heatmap","Hologram","Holographic","InkFlow","Irradiance","LiquidMetal","Marble","MeshGradient","Nebula","Obsidian","PixelThrow","Plastic","Prism","ReactionDiffusion","Satin","Sparkle","StudioBackground","Surface3D","TimeTrail","Voxels","Water","Waveform"],"documented":true,"source":{"file":"std/math.ts","line":562},"kind":"function","signature":"local(e: Expr, hint: string): Expr","params":[{"name":"e","type":"Expr","optional":false},{"name":"hint","type":"string","optional":false}]},"math.softPlus":{"id":"math.softPlus","name":"softPlus","qualifiedName":"softPlus","category":"math","module":"std/math.ts","summary":"A soft hinge: about 0 for values well below zero, about `x` above, rounded near zero.","description":"`k` is the width of the rounded corner. Use it where a hard `max(x, 0)` would show a visible crease.","examples":["const past = local(softPlus(sub(beam.along, beam.length), mul(softness, 0.6)), 'past')"],"tips":[],"see":["math.max","math.smoothstep"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.softPlus"},"usedBy":["Prism"],"documented":true,"source":{"file":"std/math.ts","line":579},"kind":"function","signature":"softPlus(x: Expr, k: Val): Expr","params":[{"name":"x","type":"Expr","optional":false},{"name":"k","type":"Val","optional":false}]},"math.gaussBell":{"id":"math.gaussBell","name":"gaussBell","qualifiedName":"gaussBell","category":"math","module":"std/math.ts","summary":"A bell curve: 1 at zero, falling toward 0 on both sides.","description":"`k` sets the width. Larger values give a narrower bell. Pass a distance or a centered coordinate as `x`.","examples":["const profile = gaussBell(div(beam.across, beamWidth), 2.5)"],"tips":[],"see":["math.exp","math.smoothstep"],"import":{"statement":"import {math} from 'shaders/std'","usage":"math.gaussBell"},"usedBy":["Prism","Waveform"],"documented":true,"source":{"file":"std/math.ts","line":597},"kind":"function","signature":"gaussBell(x: Expr, k: Val): Expr","params":[{"name":"x","type":"Expr","optional":false},{"name":"k","type":"Val","optional":false}]},"motion.phase":{"id":"motion.phase","name":"phase","qualifiedName":"phase","category":"motion","module":"std/motion.ts","summary":"How far through a repeating cycle the clock is, 0..1.","description":"`period` is the cycle length in seconds. The value ramps from 0 to 1 and snaps back.","examples":["const sweep = motion.phase(animatedTime(params), params.uniforms.period)"],"tips":[],"see":["motion.cycle","motion.oscillate"],"import":{"statement":"import {motion} from 'shaders/std'","usage":"motion.phase"},"usedBy":[],"documented":true,"source":{"file":"std/motion.ts","line":36},"kind":"function","signature":"phase(t: Expr, period: Expr | number): Expr","params":[{"name":"t","type":"Expr","optional":false},{"name":"period","type":"Expr | number","optional":false}]},"motion.cycle":{"id":"motion.cycle","name":"cycle","qualifiedName":"cycle","category":"motion","module":"std/motion.ts","summary":"A repeating cycle as two numbers: `progress` (0..1 through the current cycle) and `index` (which cycle this is).","description":"`period` is the cycle length in seconds. The \"every N seconds\" word: fade something in with `progress`, and feed `index` to `cycleSeed` when each pass should look different.","examples":["const {progress, index} = motion.cycle(animatedTime(params), 2) // a new pass every 2 s"],"tips":[],"see":["motion.cycleSeed","motion.phase","motion.pulseTrain"],"import":{"statement":"import {motion} from 'shaders/std'","usage":"motion.cycle"},"usedBy":[],"documented":true,"source":{"file":"std/motion.ts","line":54},"kind":"function","signature":"cycle(t: Expr, period: Expr | number): {progress: Expr; index: Expr}","params":[{"name":"t","type":"Expr","optional":false},{"name":"period","type":"Expr | number","optional":false}]},"motion.cycleSeed":{"id":"motion.cycleSeed","name":"cycleSeed","qualifiedName":"cycleSeed","category":"motion","module":"std/motion.ts","summary":"A random but stable number, 0..1, for each cycle index.","description":"The same index always gives the same value, so a meteor keeps its angle for the whole pass and picks a new one next time. Change `salt` for a second, independent draw.","examples":["const hue = motion.cycleSeed(index)\nconst size = motion.cycleSeed(index, 1)"],"tips":[],"see":["motion.cycle"],"import":{"statement":"import {motion} from 'shaders/std'","usage":"motion.cycleSeed"},"usedBy":[],"documented":true,"source":{"file":"std/motion.ts","line":72},"kind":"function","signature":"cycleSeed(index: Expr, salt: number = 0): Expr","params":[{"name":"index","type":"Expr","optional":false},{"name":"salt","type":"number","optional":true,"defaultValue":"0"}]},"motion.pulseTrain":{"id":"motion.pulseTrain","name":"pulseTrain","qualifiedName":"pulseTrain","category":"motion","module":"std/motion.ts","summary":"An on/off rhythm: 1 for the first part of each cycle, 0 for the rest.","description":"`period` is seconds per cycle and `duty` is the on fraction, 0..1. `soften` eases both edges over that fraction of the cycle. Leave it at 0 for a hard blink.","examples":["const blink = motion.pulseTrain(animatedTime(params), {period: 1.5, duty: 0.3, soften: 0.05})"],"tips":[],"see":["motion.pulsing","motion.cycle","motion.oscillate"],"import":{"statement":"import {motion} from 'shaders/std'","usage":"motion.pulseTrain"},"usedBy":[],"documented":true,"source":{"file":"std/motion.ts","line":88},"kind":"function","signature":"pulseTrain(t: Expr, opts: {period: Expr | number; duty: Expr | number; soften?: number}): Expr","params":[{"name":"t","type":"Expr","optional":false},{"name":"opts","type":"{period: Expr | number; duty: Expr | number; soften?: number}","optional":false}]},"motion.oscillate":{"id":"motion.oscillate","name":"oscillate","qualifiedName":"oscillate","category":"motion","module":"std/motion.ts","summary":"A smooth back-and-forth between `min` and `max`.","description":"`rate` is cycles per second, default 1. `min` and `max` default to 0 and 1. `offset` shifts the starting point, in radians, so two oscillations can run out of step.","examples":["const breathe = motion.oscillate(animatedTime(params), {rate: 0.5, min: 0.6, max: 1})"],"tips":[],"see":["motion.oscillating","motion.pulseTrain","motion.phase"],"import":{"statement":"import {motion} from 'shaders/std'","usage":"motion.oscillate"},"usedBy":[],"documented":true,"source":{"file":"std/motion.ts","line":109},"kind":"function","signature":"oscillate(t: Expr, opts: {rate?: number; min?: Expr | number; max?: Expr | number; offset?: number}): Expr","params":[{"name":"t","type":"Expr","optional":false},{"name":"opts","type":"{rate?: number; min?: Expr | number; max?: Expr | number; offset?: number}","optional":false}]},"motion.flashes":{"id":"motion.flashes","name":"flashes","qualifiedName":"flashes","category":"motion","module":"std/motion.ts","summary":"Short, bright flashes at a steady rate: dark most of the time, peaking briefly to 1.","description":"`rate` is how fast it runs (radians per second of the clock, so about 1 is one flash every six seconds). `offset` shifts when the flashes land, in radians; give each element its own (a hash times 2π) so they don't flash together. `sharpness` sets how brief each flash is: 2, 4, 8 or 16, where larger is briefer.","examples":["const blink = motion.flashes(animatedTime(params), {rate: 1.4, offset: mul(seed, 6.283), sharpness: 8})"],"tips":[],"see":["motion.pulseTrain","motion.oscillate"],"import":{"statement":"import {motion} from 'shaders/std'","usage":"motion.flashes"},"usedBy":["Sparkle"],"documented":true,"source":{"file":"std/motion.ts","line":130},"kind":"function","signature":"flashes(t: Expr, opts: {rate: Expr | number; offset?: Expr | number; sharpness: 2 | 4 | 8 | 16}): Expr","params":[{"name":"t","type":"Expr","optional":false},{"name":"opts","type":"{rate: Expr | number; offset?: Expr | number; sharpness: 2 | 4 | 8 | 16}","optional":false}]},"motion.easeIn":{"id":"motion.easeIn","name":"easeIn","qualifiedName":"easeIn","category":"motion","module":"std/motion.ts","summary":"A 0..1 value that starts slow and speeds up.","description":"","examples":["const grow = motion.easeIn(progress)"],"tips":[],"see":["motion.easeOut","motion.easeInOut"],"import":{"statement":"import {motion} from 'shaders/std'","usage":"motion.easeIn"},"usedBy":[],"documented":true,"source":{"file":"std/motion.ts","line":148},"kind":"function","signature":"easeIn(x: Expr): Expr","params":[{"name":"x","type":"Expr","optional":false}]},"motion.easeOut":{"id":"motion.easeOut","name":"easeOut","qualifiedName":"easeOut","category":"motion","module":"std/motion.ts","summary":"A 0..1 value that starts fast and slows to a stop.","description":"","examples":["const settle = motion.easeOut(progress)"],"tips":[],"see":["motion.easeIn","motion.easeInOut"],"import":{"statement":"import {motion} from 'shaders/std'","usage":"motion.easeOut"},"usedBy":[],"documented":true,"source":{"file":"std/motion.ts","line":158},"kind":"function","signature":"easeOut(x: Expr): Expr","params":[{"name":"x","type":"Expr","optional":false}]},"motion.easeInOut":{"id":"motion.easeInOut","name":"easeInOut","qualifiedName":"easeInOut","category":"motion","module":"std/motion.ts","summary":"A 0..1 value that starts slow, speeds up, and slows again at the end.","description":"","examples":["const glide = motion.easeInOut(progress)"],"tips":[],"see":["motion.easeIn","motion.easeOut"],"import":{"statement":"import {motion} from 'shaders/std'","usage":"motion.easeInOut"},"usedBy":[],"documented":true,"source":{"file":"std/motion.ts","line":168},"kind":"function","signature":"easeInOut(x: Expr): Expr","params":[{"name":"x","type":"Expr","optional":false}]},"motion.oscillating":{"id":"motion.oscillating","name":"oscillating","qualifiedName":"oscillating","category":"motion","module":"std/motion.ts","summary":"A knob that sweeps between `min` and `max` on its own, for any slot that takes a prop.","description":"Same options as `oscillate`. By default it follows the page clock. Pass `clock: 'node'` to follow the layer's own speed prop instead, after declaring `animatedTime` on the definition.","examples":["map: warps.twirl({center: p('center'), intensity: motion.oscillating({rate: 0.25, min: 0.4, max: 1.6, clock: 'node'})})"],"tips":["Inside `map:` it always follows the layer's clock, so declare `animatedTime: {speed: 'speed'}` there."],"see":["motion.oscillate","motion.pulsing"],"import":{"statement":"import {motion} from 'shaders/std'","usage":"motion.oscillating"},"usedBy":[],"documented":true,"source":{"file":"std/motion.ts","line":194},"kind":"function","signature":"oscillating(opts: {rate?: number; min?: Expr | number; max?: Expr | number; offset?: number; clock?: SignalClock}): Scalar","params":[{"name":"opts","type":"{rate?: number; min?: Expr | number; max?: Expr | number; offset?: number; clock?: SignalClock}","optional":false}]},"motion.pulsing":{"id":"motion.pulsing","name":"pulsing","qualifiedName":"pulsing","category":"motion","module":"std/motion.ts","summary":"A knob that switches on and off in a rhythm, for any slot that takes a prop.","description":"`period` is seconds per cycle, `duty` the on fraction, `soften` the edge ease, as in `pulseTrain`. Follows the page clock unless `clock: 'node'` is set.","examples":["effect: effects.color.saturate(motion.pulsing({period: 2, duty: 0.5, soften: 0.1})) // color, then gray, then color"],"tips":["Inside `map:` it always follows the layer's clock, so declare `animatedTime: {speed: 'speed'}` there."],"see":["motion.pulseTrain","motion.oscillating"],"import":{"statement":"import {motion} from 'shaders/std'","usage":"motion.pulsing"},"usedBy":[],"documented":true,"source":{"file":"std/motion.ts","line":211},"kind":"function","signature":"pulsing(opts: {period: number; duty: number; soften?: number; clock?: SignalClock}): Scalar","params":[{"name":"opts","type":"{period: number; duty: number; soften?: number; clock?: SignalClock}","optional":false}]},"signal.pointer":{"id":"signal.pointer","name":"pointer","qualifiedName":"pointer","category":"signal","module":"std/signal.ts","summary":"The pointer's position, for a simulation step that reacts to the cursor.","description":"The teleport guard is on by default: when the cursor jumps a long way in one frame, the jump adds no motion, so a cursor entering the canvas does not slam the simulation.","examples":["op.splat({at: pointer({teleportGuard: 'on'}), amount: pointerSpeed({max: 2}), radius: p('radius')})"],"tips":["Keep the default `'on'` for cursor effects. Use `'off'` only when a jump should count as a stroke — a pointer that is set by code rather than a real cursor, for example."],"see":["signal.pointerSpeed"],"import":{"statement":"import {signal} from 'shaders/std'","usage":"signal.pointer"},"usedBy":["CursorRipples"],"documented":true,"source":{"file":"std/signal.ts","line":39},"kind":"function","signature":"pointer(opts?: {teleportGuard?: 'on' | 'off'}): PointerSignal","params":[{"name":"opts","type":"{teleportGuard?: 'on' | 'off'}","optional":true}]},"signal.pointerSpeed":{"id":"signal.pointerSpeed","name":"pointerSpeed","qualifiedName":"pointerSpeed","category":"signal","module":"std/signal.ts","summary":"The pointer's speed, capped at `max`, as the strength of a simulation step.","description":"Speed is in uv per second. A still cursor gives 0, a flick across the canvas gives a value near `max`. Default cap is 2.","examples":["op.splat({at: pointer(), amount: pointerSpeed({max: 2}), radius: p('radius')})"],"tips":[],"see":["signal.pointer"],"import":{"statement":"import {signal} from 'shaders/std'","usage":"signal.pointerSpeed"},"usedBy":["CursorRipples"],"documented":true,"source":{"file":"std/signal.ts","line":66},"kind":"function","signature":"pointerSpeed(opts?: {max?: number}): PointerSpeedSignal","params":[{"name":"opts","type":"{max?: number}","optional":true}]},"signal.cpuHash01":{"id":"signal.cpuHash01","name":"cpuHash01","qualifiedName":"cpuHash01","category":"signal","module":"std/signal.ts","summary":"A random but stable number, 0..1, from a seed, computed in JavaScript.","description":"Use it when a per-seed constant is worked out once on the CPU rather than at every pixel, such as the size and drift of each light in a set.","examples":["const radiusX = cpuHash01(seed) * 0.5 + 0.2\nconst radiusY = cpuHash01(seed + 37) * 0.25 + 0.1"],"tips":[],"see":["signal.pointer"],"import":{"statement":"import {signal} from 'shaders/std'","usage":"signal.cpuHash01"},"usedBy":["StudioBackground"],"documented":true,"source":{"file":"std/signal.ts","line":114},"kind":"function","signature":"cpuHash01(x: number): number","params":[{"name":"x","type":"number","optional":false}]},"filter.tintToward":{"id":"filter.tintToward","name":"tintToward","qualifiedName":"tintToward","category":"filter","module":"std/filter.ts","summary":"Mix the child's color toward one color by an amount, leaving its alpha alone.","description":"`color` is a color prop. `amount` runs 0 (no change) to 1 (fully that color) and can be a number, a prop, or a value that varies per pixel such as a mask. Pointwise.","examples":["effect: tintToward(p('color'), {amount: radialMask({center: p('center'), radius: p('radius'), falloff: p('falloff')}).times(p('intensity'))})"],"tips":["Pair it with `identityWhen: isZero('intensity')` so the filter costs nothing at zero."],"see":["filter.paintThrough","filter.pointwise"],"import":{"statement":"import {tintToward} from 'shaders/std'","usage":"tintToward"},"usedBy":["Vignette"],"documented":true,"source":{"file":"std/filter.ts","line":47},"kind":"function","signature":"tintToward(color: PropRef, opts: {amount: ScalarInput}): TintTowardEffect","params":[{"name":"color","type":"PropRef","optional":false},{"name":"opts","type":"{amount: ScalarInput}","optional":false}]},"filter.displaceBy":{"id":"filter.displaceBy","name":"displaceBy","qualifiedName":"displaceBy","category":"filter","module":"std/filter.ts","summary":"Push the child's pixels around by a vector field from one of your simulations.","description":"`field` is a simulation output, such as `waves.output('displacement')` from `simulate.grid`. `strength` scales the push (the library uses 0–20), capped at about 15% of the canvas. `chromatic` (0–3) splits red and blue slightly apart from green along the push, like a lens. `edges` is a prop marked `compileTime: true` holding `'stretch'`, `'transparent'`, `'mirror'` or `'wrap'`. Gather.","examples":["effect: displaceBy(waves.output('displacement'), {strength: p('intensity'), chromatic: p('chromaticSplit'), edges: p('edges')})"],"tips":["The simulation must derive the field with `op.gradient()`; the definition fails loudly otherwise."],"see":["filter.gather"],"import":{"statement":"import {displaceBy} from 'shaders/std'","usage":"displaceBy"},"usedBy":["CursorRipples"],"documented":true,"source":{"file":"std/filter.ts","line":80},"kind":"function","signature":"displaceBy(field: import('./sim').SimOutputRef, opts: {strength: PropRef; chromatic: PropRef; edges: PropRef}): DisplaceByEffect","params":[{"name":"field","type":"import('./sim').SimOutputRef","optional":false},{"name":"opts","type":"{strength: PropRef; chromatic: PropRef; edges: PropRef}","optional":false}]},"filter.pointwise":{"id":"filter.pointwise","name":"pointwise","qualifiedName":"pointwise","category":"filter","module":"std/filter.ts","summary":"Write your own pointwise effect: a function of the child's color at this pixel.","description":"Give it `build`, which receives `params.childNode` (the child's color here) and `params.uniforms` (your props), and returns the new color. Keep the child's alpha unless you mean to change it. No texture is involved, so it is the cheapest kind of filter.","examples":["effect: pointwise({build: ({childNode, uniforms}) => math.vec4(math.mul(childNode.member('rgb'), uniforms.gain), childNode.member('a'))})"],"tips":[],"see":["filter.gather","filter.tintToward","filter.paintThrough"],"import":{"statement":"import {pointwise} from 'shaders/std'","usage":"pointwise"},"usedBy":[],"documented":true,"source":{"file":"std/filter.ts","line":105},"kind":"function","signature":"pointwise(config: Omit<PointwiseEffect, 'kind'>): PointwiseEffect","params":[{"name":"config","type":"Omit<PointwiseEffect, 'kind'>","optional":false}]},"filter.gather":{"id":"filter.gather","name":"gather","qualifiedName":"gather","category":"filter","module":"std/filter.ts","summary":"Write your own gather effect: a function that can read the child anywhere.","description":"Give it `build`, which receives the child as a texture (`params.texture`, or `params.sampleStraight(uv)` for plain colors), `params.ctx.uv` for this pixel and `params.uniforms` for your props. Return the new color. Set `resultAlpha: 'straight'` when you built the color from `sampleStraight`. Costs one extra render of the child.","examples":["effect: gather({resultAlpha: 'straight', build: ({sampleStraight, ctx, uniforms}) => sampleStraight(math.add(ctx.uv, uniforms.shift))})"],"tips":["Reach for `pointwise` first. Only gather when the effect needs pixels other than this one."],"see":["filter.pointwise","filter.displaceBy"],"import":{"statement":"import {gather} from 'shaders/std'","usage":"gather"},"usedBy":[],"documented":true,"source":{"file":"std/filter.ts","line":127},"kind":"function","signature":"gather(config: Omit<GatherEffect, 'kind'>): GatherEffect","params":[{"name":"config","type":"Omit<GatherEffect, 'kind'>","optional":false}]},"filter.paintThrough":{"id":"filter.paintThrough","name":"paintThrough","qualifiedName":"paintThrough","category":"filter","module":"std/filter.ts","summary":"Show a paint through the child, so the child becomes the mask.","description":"Like CSS `mask-image` the other way round: the paint is the content and the child decides where it shows. `by: 'alpha'` (the default) reveals the paint wherever the child is opaque, which gives a gradient through text. `by: 'luminance'` reveals it where the child is bright. The paint's own alpha still applies. Pointwise.","examples":["effect: paintThrough(paint.rampOver(paint.dist.radial({center: p('center'), radius: p('radius'), aspect: 1, skew: 0}), paint.pair(p('colorA'), p('colorB'), p('colorSpace'))))"],"tips":[],"see":["filter.tintToward","filter.pointwise"],"import":{"statement":"import {paintThrough} from 'shaders/std'","usage":"paintThrough"},"usedBy":[],"documented":true,"source":{"file":"std/filter.ts","line":148},"kind":"function","signature":"paintThrough(paint: Paint, opts: {by?: 'alpha' | 'luminance'} = {}): PointwiseEffect","params":[{"name":"paint","type":"Paint","optional":false},{"name":"opts","type":"{by?: 'alpha' | 'luminance'}","optional":true,"defaultValue":"{}"}]},"effects.color.hueRotate":{"id":"effects.color.hueRotate","name":"hueRotate","qualifiedName":"hueRotate","category":"effects.color","module":"std/effects/color.ts","summary":"Rotate the child's hue around the color wheel.","description":"`shift` is in degrees. 0 leaves the color unchanged, 180 swaps every color for its complement, 360 comes back around. Alpha kept.","examples":["effect: hueRotate(p('shift'))"],"tips":["Declare `identityWhen: isZero('shift')` so the filter is skipped at 0."],"see":["effects.color.saturate","effects.color.vibrance","effects.color.tint"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.color.hueRotate"},"usedBy":["HueShift"],"documented":true,"source":{"file":"std/effects/color.ts","line":36},"kind":"function","signature":"hueRotate(shift: ArgSpec): PointwiseEffect","params":[{"name":"shift","type":"ArgSpec","optional":false}]},"effects.color.saturate":{"id":"effects.color.saturate","name":"saturate","qualifiedName":"saturate","category":"effects.color","module":"std/effects/color.ts","summary":"Scale the child's saturation.","description":"`intensity` is a multiplier: 0 is grayscale, 1 leaves the color unchanged, values above 1 (up to about 3) push colors past their original saturation.","examples":["effect: saturate(p('intensity'))"],"tips":["Declare `identityWhen: isValue('intensity', 1)`; the no-change value is 1, not 0."],"see":["effects.color.vibrance","effects.color.grayscale"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.color.saturate"},"usedBy":["Saturation"],"documented":true,"source":{"file":"std/effects/color.ts","line":54},"kind":"function","signature":"saturate(intensity: ArgSpec): PointwiseEffect","params":[{"name":"intensity","type":"ArgSpec","optional":false}]},"effects.color.vibrance":{"id":"effects.color.vibrance","name":"vibrance","qualifiedName":"vibrance","category":"effects.color","module":"std/effects/color.ts","summary":"Boost muted colors while leaving already-vivid ones mostly alone.","description":"`intensity` is an adjustment around 0 (about −2 to 2): 0 leaves the color unchanged, positive values add saturation to dull pixels, negative values drain it.","examples":["effect: vibrance(p('intensity'))"],"tips":["Declare `identityWhen: isZero('intensity')`."],"see":["effects.color.saturate"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.color.vibrance"},"usedBy":["Vibrance"],"documented":true,"source":{"file":"std/effects/color.ts","line":72},"kind":"function","signature":"vibrance(intensity: ArgSpec): PointwiseEffect","params":[{"name":"intensity","type":"ArgSpec","optional":false}]},"effects.color.brightnessContrast":{"id":"effects.color.brightnessContrast","name":"brightnessContrast","qualifiedName":"brightnessContrast","category":"effects.color","module":"std/effects/color.ts","summary":"Adjust the child's brightness and contrast together.","description":"Both are sliders from −1 to 1 with 0 as no change. `brightness` adds to every channel. `contrast` stretches colors away from mid-gray (positive) or flattens them toward it (negative).","examples":["effect: brightnessContrast({brightness: p('brightness'), contrast: p('contrast')})"],"tips":["Declare `identityWhen: allOf(isZero('brightness'), isZero('contrast'))`."],"see":["effects.color.exposure"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.color.brightnessContrast"},"usedBy":["BrightnessContrast"],"documented":true,"source":{"file":"std/effects/color.ts","line":91},"kind":"function","signature":"brightnessContrast(slots: {brightness: ArgSpec; contrast: ArgSpec}): PointwiseEffect","params":[{"name":"slots","type":"{brightness: ArgSpec; contrast: ArgSpec}","optional":false}]},"effects.color.exposure":{"id":"effects.color.exposure","name":"exposure","qualifiedName":"exposure","category":"effects.color","module":"std/effects/color.ts","summary":"Multiply the child's brightness, like opening a camera's aperture.","description":"`gain` of 1 leaves the color unchanged, 0.5 halves it, 2 doubles it. Blacks stay black. Values pushed above 1 are not clamped, so a glow or bloom filter placed after this one can pick them up.","examples":["effect: exposure(p('exposure'))"],"tips":["Declare `identityWhen: isValue('exposure', 1)`; the no-change value is 1."],"see":["effects.color.brightnessContrast"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.color.exposure"},"usedBy":["Exposure"],"documented":true,"source":{"file":"std/effects/color.ts","line":110},"kind":"function","signature":"exposure(gain: ArgSpec): PointwiseEffect","params":[{"name":"gain","type":"ArgSpec","optional":false}]},"effects.color.grayscale":{"id":"effects.color.grayscale","name":"grayscale","qualifiedName":"grayscale","category":"effects.color","module":"std/effects/color.ts","summary":"Turn the child black and white, weighted the way the eye sees brightness.","description":"Alpha kept.","examples":["effect: grayscale()"],"tips":[],"see":["effects.color.saturate","effects.color.duotone"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.color.grayscale"},"usedBy":["Grayscale"],"documented":true,"source":{"file":"std/effects/color.ts","line":124},"kind":"function","signature":"grayscale(): PointwiseEffect","params":[]},"effects.color.invert":{"id":"effects.color.invert","name":"invert","qualifiedName":"invert","category":"effects.color","module":"std/effects/color.ts","summary":"Flip every color of the child to its negative.","description":"Alpha kept.","examples":["effect: invert()"],"tips":[],"see":["effects.color.solarize"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.color.invert"},"usedBy":["Invert"],"documented":true,"source":{"file":"std/effects/color.ts","line":138},"kind":"function","signature":"invert(): PointwiseEffect","params":[]},"effects.color.solarize":{"id":"effects.color.solarize","name":"solarize","qualifiedName":"solarize","category":"effects.color","module":"std/effects/color.ts","summary":"Invert only the bright parts of the child, the darkroom solarization look.","description":"`threshold` (0–1) is the brightness above which a pixel flips. `strength` (0–1) fades the flipped result over the original: 0 is untouched, 1 is fully solarized.","examples":["effect: solarize({threshold: p('threshold'), strength: p('strength')})"],"tips":["Declare `identityWhen: isZero('strength')`."],"see":["effects.color.invert","effects.color.posterize"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.color.solarize"},"usedBy":["Solarize"],"documented":true,"source":{"file":"std/effects/color.ts","line":155},"kind":"function","signature":"solarize(slots: {threshold: ArgSpec; strength: ArgSpec}): PointwiseEffect","params":[{"name":"slots","type":"{threshold: ArgSpec; strength: ArgSpec}","optional":false}]},"effects.color.posterize":{"id":"effects.color.posterize","name":"posterize","qualifiedName":"posterize","category":"effects.color","module":"std/effects/color.ts","summary":"Reduce the child to a few flat color levels, like a screen-printed poster.","description":"`steps` is the number of levels per channel; 2 to 20 is the useful range. Fewer steps give bolder bands.","examples":["effect: posterize(p('levels'))"],"tips":[],"see":["effects.color.solarize","effects.color.isolines"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.color.posterize"},"usedBy":["Posterize"],"documented":true,"source":{"file":"std/effects/color.ts","line":172},"kind":"function","signature":"posterize(steps: ArgSpec): PointwiseEffect","params":[{"name":"steps","type":"ArgSpec","optional":false}]},"effects.color.tint":{"id":"effects.color.tint","name":"tint","qualifiedName":"tint","category":"effects.color","module":"std/effects/color.ts","summary":"Wash the child toward one color.","description":"`amount` is 0–1: 0 leaves the child alone, 1 replaces its color entirely. While the boolean `preserveLuminosity` prop is on, the result is scaled back to the child's original brightness, so the tint changes hue without darkening. Declare that prop `compileTime: true` with `transform: (v) => (v ? 1 : 0)`. Alpha kept.","examples":["effect: tint({color: p('color'), amount: p('amount'), preserveLuminosity: p('preserveLuminosity')})"],"tips":["Reach for `tintToward` instead when you do not need the brightness-preserving mode."],"see":["filter.tintToward","effects.color.duotone","effects.color.hueRotate"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.color.tint"},"usedBy":["Tint"],"documented":true,"source":{"file":"std/effects/color.ts","line":192},"kind":"function","signature":"tint(slots: {color: PropRef; amount: ArgSpec; preserveLuminosity: PropRef}): PointwiseEffect","params":[{"name":"slots","type":"{color: PropRef; amount: ArgSpec; preserveLuminosity: PropRef}","optional":false}]},"effects.color.filmGrain":{"id":"effects.color.filmGrain","name":"filmGrain","qualifiedName":"filmGrain","category":"effects.color","module":"std/effects/color.ts","summary":"Sprinkle film grain over the child, heaviest in the shadows.","description":"`strength` (0–1) sets how visible the grain is. `bias` (0–10) concentrates it in darker areas; 0 grains everything evenly. While the boolean `animated` prop is on the pattern changes every frame; off, it freezes. The definition must declare the grain's clock: `extraFields: {animTime: {schema: schema.f32, initial: 0}}`. Alpha kept.","examples":["effect: filmGrain({strength: p('strength'), bias: p('bias'), animated: p('animated')})"],"tips":["Declare `identityWhen: isZero('strength')`."],"see":["effects.color.posterize"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.color.filmGrain"},"usedBy":["FilmGrain"],"documented":true,"source":{"file":"std/effects/color.ts","line":220},"kind":"function","signature":"filmGrain(slots: {strength: ArgSpec; bias: ArgSpec; animated: PropRef}): PointwiseEffect","params":[{"name":"slots","type":"{strength: ArgSpec; bias: ArgSpec; animated: PropRef}","optional":false}]},"effects.color.isolines":{"id":"effects.color.isolines","name":"isolines","qualifiedName":"isolines","category":"effects.color","module":"std/effects/color.ts","summary":"Draw topographic contour lines over the child, traced from its brightness or its alpha.","description":"`levels` is how many contours (2–30). `lineWidth` is in pixels. `softness` (0–1) blurs the line edges. `gamma` crowds the contours toward the highlights (below 1) or the shadows (above 1). The boolean `invert` prop flips the source. `source` (`'luminance' | 'alpha'`) and `colorMode` (`'source' | 'custom'`) are select props declared `compileTime: true`: in source mode the lines take the child's own colors over transparent, in custom mode they take `lineColor` over `backgroundColor`.","examples":["effect: isolines({source: p('source'), levels: p('levels'), lineWidth: p('lineWidth'), softness: p('softness'), gamma: p('gamma'), invert: p('invert'), colorMode: p('colorMode'), lineColor: p('lineColor'), backgroundColor: p('backgroundColor')})"],"tips":["The lines replace the child rather than sit over it; set `blendWithChildren: false` in the definition."],"see":["effects.color.posterize","effects.color.duotone"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.color.isolines"},"usedBy":["ContourLines"],"documented":true,"source":{"file":"std/effects/color.ts","line":269},"kind":"function","signature":"isolines(slots: {\n    source: PropRef\n    levels: ArgSpec\n    lineWidth: ArgSpec\n    softness: ArgSpec\n    gamma: ArgSpec\n    invert: ArgSpec\n    colorMode: PropRef\n    lineColor: PropRef\n    backgroundColor: PropRef\n}): PointwiseEffect","params":[{"name":"slots","type":"{\n    source: PropRef\n    levels: ArgSpec\n    lineWidth: ArgSpec\n    softness: ArgSpec\n    gamma: ArgSpec\n    invert: ArgSpec\n    colorMode: PropRef\n    lineColor: PropRef\n    backgroundColor: PropRef\n}","optional":false}]},"effects.color.duotone":{"id":"effects.color.duotone","name":"duotone","qualifiedName":"duotone","category":"effects.color","module":"std/effects/color.ts","summary":"Repaint the child in two colors: `colorA` for the shadows, `colorB` for the highlights.","description":"`blend` (0–1) is the brightness where the two meet. `colorSpace` is a select prop with `transform: transformColorSpace` and `compileTime: true`; it picks the space the colors mix in (`'oklch'` for vivid, `'linear'` for plain). Alpha kept.","examples":["effect: duotone({colorA: p('colorA'), colorB: p('colorB'), blend: p('blend'), colorSpace: p('colorSpace')})"],"tips":[],"see":["effects.color.tritone","effects.color.gradientMap","effects.color.grayscale"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.color.duotone"},"usedBy":["Duotone"],"documented":true,"source":{"file":"std/effects/color.ts","line":321},"kind":"function","signature":"duotone(slots: {colorA: PropRef; colorB: PropRef; blend: ArgSpec; colorSpace: PropRef}): PointwiseEffect","params":[{"name":"slots","type":"{colorA: PropRef; colorB: PropRef; blend: ArgSpec; colorSpace: PropRef}","optional":false}]},"effects.color.tritone":{"id":"effects.color.tritone","name":"tritone","qualifiedName":"tritone","category":"effects.color","module":"std/effects/color.ts","summary":"Repaint the child in three colors: `colorA` for the shadows, `colorB` for the midtones, `colorC` for the highlights.","description":"`blendMid` (0–1) is the brightness the midtone color sits at. `colorSpace` is a select prop with `transform: transformColorSpace` and `compileTime: true`; it picks the space the colors mix in. Alpha kept.","examples":["effect: tritone({colorA: p('colorA'), colorB: p('colorB'), colorC: p('colorC'), blendMid: p('blendMid'), colorSpace: p('colorSpace')})"],"tips":[],"see":["effects.color.duotone","effects.color.gradientMap"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.color.tritone"},"usedBy":["Tritone"],"documented":true,"source":{"file":"std/effects/color.ts","line":346},"kind":"function","signature":"tritone(slots: {colorA: PropRef; colorB: PropRef; colorC: PropRef; blendMid: ArgSpec; colorSpace: PropRef}): PointwiseEffect","params":[{"name":"slots","type":"{colorA: PropRef; colorB: PropRef; colorC: PropRef; blendMid: ArgSpec; colorSpace: PropRef}","optional":false}]},"effects.color.gradientMap":{"id":"effects.color.gradientMap","name":"gradientMap","qualifiedName":"gradientMap","category":"effects.color","module":"std/effects/color.ts","summary":"Recolor the child by its brightness through a gradient, the Photoshop gradient map.","description":"`palette` is a select prop declared `compileTime: true`: one of the built-in gradients `'rainbow' | 'sunset' | 'ocean' | 'fire' | 'pastel' | 'neon'`, or `'custom'` to use `colorLow`, `colorMid` and `colorHigh` (shadows, midtones, highlights) mixed in `colorSpace`. `blackPoint` and `whitePoint` (0–1) clip the input brightness. `contrast` (0–3) steepens the mapping. `speed` scrolls the gradient over time; 0 holds it still. `strength` (0–1) fades the result over the original. Alpha kept.","examples":["effect: gradientMap({palette: p('palette'), colorLow: p('colorLow'), colorMid: p('colorMid'), colorHigh: p('colorHigh'), speed: p('speed'), contrast: p('contrast'), blackPoint: p('blackPoint'), whitePoint: p('whitePoint'), strength: p('strength'), colorSpace: p('colorSpace')})"],"tips":["Declare `identityWhen: isZero('strength')`."],"see":["effects.color.duotone","effects.color.tritone"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.color.gradientMap"},"usedBy":["GradientMap"],"documented":true,"source":{"file":"std/effects/color.ts","line":400},"kind":"function","signature":"gradientMap(slots: {\n    palette: PropRef\n    colorLow: PropRef\n    colorMid: PropRef\n    colorHigh: PropRef\n    speed: PropRef\n    contrast: ArgSpec\n    blackPoint: ArgSpec\n    whitePoint: ArgSpec\n    strength: PropRef\n    colorSpace: PropRef\n}): PointwiseEffect","params":[{"name":"slots","type":"{\n    palette: PropRef\n    colorLow: PropRef\n    colorMid: PropRef\n    colorHigh: PropRef\n    speed: PropRef\n    contrast: ArgSpec\n    blackPoint: ArgSpec\n    whitePoint: ArgSpec\n    strength: PropRef\n    colorSpace: PropRef\n}","optional":false}]},"effects.color.lutAtlasGrade":{"id":"effects.color.lutAtlasGrade","name":"lutAtlasGrade","qualifiedName":"lutAtlasGrade","category":"effects.color","module":"std/effects/color.ts","summary":"Grade a color through a measured 3D color lookup table (LUT), the film-stock look.","description":"Returns a stage `(color, params) => gradedColor` for a `gpu:` fragment, not an `effect:`. `select` is a select prop declared `compileTime: true` that names the table; `decode(key)` returns that table's bytes as RGBA, `size` levels per channel laid out as `size` square slices side by side (red across, green down, one slice per blue level), sRGB in and out. `fallback` is the key used when the prop is unset. `strength` (0–1) fades the graded color over the original. Alpha kept; colors brighter than white grade as white.","examples":["const grade = lutAtlasGrade({select: p('stock'), strength: p('strength'), size: 17, decode: decodeStockLut, fallback: 'portrait400', label: 'film-lut'})\n// then, inside the definition's gpu.fragment: grade(childColor, params)"],"tips":["Changing `select` rebuilds the shader; keep the table list short."],"see":["effects.color.tint","effects.color.gradientMap"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.color.lutAtlasGrade"},"usedBy":["FilmStock"],"documented":true,"source":{"file":"std/effects/color.ts","line":534},"kind":"function","signature":"lutAtlasGrade(slots: {\n    select: PropRef\n    strength: PropRef\n    size: number\n    decode: (key: string) => Uint8Array\n    fallback: string\n    label: string\n}): (color: Expr, params: FilterParams | GpuFragmentParams) => Expr","params":[{"name":"slots","type":"{\n    select: PropRef\n    strength: PropRef\n    size: number\n    decode: (key: string) => Uint8Array\n    fallback: string\n    label: string\n}","optional":false}]},"effects.blurs.gatherStack":{"id":"effects.blurs.gatherStack","name":"gatherStack","qualifiedName":"gatherStack","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"Assemble an effect from one sampling stage and an ordered list of overlay stages.","description":"The child renders to a texture, the sampling stage reads it, and each overlay edits the result in turn. Put the result in a filter's `effect:` field. Pass `{resultAlpha: 'straight'}` only when the stack already returns straight (not premultiplied) alpha, as `crispTap` and `refractedTaps` do.","examples":["effect: gatherStack(rgbSplit({amount: p('colorShift')}), [scanlines({frequency: p('lines'), intensity: 0.3}), opaque()])"],"tips":["An empty overlay list is fine: `gatherStack(stage, [])` wraps a lone sampling stage."],"see":["SampleStage","OverlayStage","GatherFrame"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.gatherStack"},"usedBy":["CRTScreen","ChromaticAberration","DropShadow","FlutedGlass","Glitch","PagePeel","Sparkle","Spherize","VHS"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":67},"kind":"function","signature":"gatherStack(sample: SampleStage, overlays: OverlayStage[], opts?: {resultAlpha?: 'premultiplied' | 'straight'}): GatherEffect","params":[{"name":"sample","type":"SampleStage","optional":false},{"name":"overlays","type":"OverlayStage[]","optional":false},{"name":"opts","type":"{resultAlpha?: 'premultiplied' | 'straight'}","optional":true}]},"effects.blurs.rgbSplit":{"id":"effects.blurs.rgbSplit","name":"rgbSplit","qualifiedName":"rgbSplit","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"The child with its red and blue pulled a little to either side, like a misaligned CRT.","description":"`amount` is the shift; 1 moves red and blue each by 0.002 of the canvas width, 0–10 is the usual range. Returns a three-channel color, so finish the stack with `opaque`.","examples":["effect: gatherStack(rgbSplit({amount: p('colorShift')}), [opaque()])"],"tips":[],"see":["effects.blurs.glitchRgbSplit","effects.blurs.scanlines","effects.blurs.phosphorMask","effects.blurs.opaque"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.rgbSplit"},"usedBy":["CRTScreen"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":118},"kind":"function","signature":"rgbSplit(opts: {amount: ArgSpec}): SampleStage","params":[{"name":"opts","type":"{amount: ArgSpec}","optional":false}]},"effects.blurs.tapeWarp":{"id":"effects.blurs.tapeWarp","name":"tapeWarp","qualifiedName":"tapeWarp","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"The wobbling, jittering coordinates of a worn VHS tape, to feed into `chromaSmearTaps`.","description":"`wobble` (0–5) is the tape damage: waves, creases and head-switching noise that burst on and off over time. `jitter` (0–1) is fine per-scanline noise. `speed` scales the global clock, so the definition needs no `animatedTime`.","examples":["chromaSmearTaps({warp: tapeWarp({wobble: p('wobble'), jitter: p('scanlineNoise'), speed: p('speed')}), smear: p('smear')})"],"tips":[],"see":["effects.blurs.chromaSmearTaps","effects.blurs.beatPulse"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.tapeWarp"},"usedBy":["VHS"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":143},"kind":"function","signature":"tapeWarp(opts: {wobble: ArgSpec; jitter: ArgSpec; speed: ArgSpec}): (params: RttFilterParams) => Expr","params":[{"name":"opts","type":"{wobble: ArgSpec; jitter: ArgSpec; speed: ArgSpec}","optional":false}]},"effects.blurs.chromaSmearTaps":{"id":"effects.blurs.chromaSmearTaps","name":"chromaSmearTaps","qualifiedName":"chromaSmearTaps","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"The child sampled like a VHS deck: brightness stays sharp while color bleeds sideways.","description":"`warp` is the coordinate set from `tapeWarp`. `smear` sets how far color trails; positive trails it to the right, negative to the left, −2 to 2 is the usual range. Alpha comes from the sharp tap.","examples":["effect: gatherStack(chromaSmearTaps({warp: tapeWarp({wobble: p('wobble'), jitter: p('noise'), speed: p('speed')}), smear: p('smear')}), [beatPulse({wobble: p('wobble'), speed: p('speed')})])"],"tips":[],"see":["effects.blurs.tapeWarp","effects.blurs.beatPulse","effects.blurs.rgbSplit"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.chromaSmearTaps"},"usedBy":["VHS"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":165},"kind":"function","signature":"chromaSmearTaps(opts: {warp: (params: RttFilterParams) => Expr; smear: ArgSpec}): SampleStage","params":[{"name":"opts","type":"{warp: (params: RttFilterParams) => Expr; smear: ArgSpec}","optional":false}]},"effects.blurs.brightestNear":{"id":"effects.blurs.brightestNear","name":"brightestNear","qualifiedName":"brightestNear","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"The brightest color in a small ring around a point: the layer grown outward, so a spot just outside a bright area still reads as bright.","description":"`read` samples the layer at a uv and returns straight color (pass `params.sampleStraight`). `center` is the uv to look around; `radius` is the ring's radius per axis in uv, as a `vec2` (divide a pixel radius by the viewport size). `taps` points sit evenly round the ring starting at `angle` degrees, plus the centre itself. Returns the brightest of them as `color` (straight) and its brightness as `luma` (Rec.709 luminance times alpha).","examples":["const {color, luma} = brightestNear(params.sampleStraight, pointUV, {radius: div(p, viewport), taps: 4, angle: 45})"],"tips":["Thin bright details smaller than the gap between taps can slip through; more taps\nclose the gaps at one sample each."],"see":["effects.blurs.gatherStack"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.brightestNear"},"usedBy":["Sparkle"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":197},"kind":"function","signature":"brightestNear(read: (uv: Expr) => Expr, center: Expr, opts: {radius: Expr; taps: number; angle?: number}): {\n    color: Expr\n    luma: Expr\n}","params":[{"name":"read","type":"(uv: Expr) => Expr","optional":false},{"name":"center","type":"Expr","optional":false},{"name":"opts","type":"{radius: Expr; taps: number; angle?: number}","optional":false}]},"effects.blurs.adjust":{"id":"effects.blurs.adjust","name":"adjust","qualifiedName":"adjust","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"Brightness and contrast, pivoting around mid-grey.","description":"1 leaves both unchanged; 0.5–2 is the usual range. Works on the three-channel color of a stack begun with `rgbSplit`.","examples":["adjust({brightness: p('brightness'), contrast: p('contrast')})"],"tips":[],"see":["effects.blurs.rgbSplit","effects.blurs.scanlines","effects.blurs.opaque"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.adjust"},"usedBy":["CRTScreen"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":229},"kind":"function","signature":"adjust(opts: {brightness: ArgSpec; contrast: ArgSpec}): OverlayStage","params":[{"name":"opts","type":"{brightness: ArgSpec; contrast: ArgSpec}","optional":false}]},"effects.blurs.scanlines":{"id":"effects.blurs.scanlines","name":"scanlines","qualifiedName":"scanlines","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"Horizontal scanlines darkening the child in a smooth wave.","description":"`frequency` is the number of lines from top to bottom (100–800). `intensity` (0–1) is how dark the troughs get. Works on the three-channel color of a stack begun with `rgbSplit`.","examples":["scanlines({frequency: p('scanlineFrequency'), intensity: p('scanlineIntensity')})"],"tips":[],"see":["effects.blurs.distortedScanlines","effects.blurs.phosphorMask","effects.blurs.vignetteOverlay"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.scanlines"},"usedBy":["CRTScreen"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":246},"kind":"function","signature":"scanlines(opts: {frequency: ArgSpec; intensity: ArgSpec}): OverlayStage","params":[{"name":"opts","type":"{frequency: ArgSpec; intensity: ArgSpec}","optional":false}]},"effects.blurs.phosphorMask":{"id":"effects.blurs.phosphorMask","name":"phosphorMask","qualifiedName":"phosphorMask","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"A faint red, green and blue phosphor stripe pattern across the child.","description":"`pitch` sets how many stripes fit across the canvas, about half of `pitch`; 8–128 is the usual range. Works on the three-channel color of a stack begun with `rgbSplit`.","examples":["phosphorMask({pitch: p('pixelSize')})"],"tips":[],"see":["effects.blurs.scanlines","effects.blurs.rgbSplit"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.phosphorMask"},"usedBy":["CRTScreen"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":267},"kind":"function","signature":"phosphorMask(opts: {pitch: ArgSpec}): OverlayStage","params":[{"name":"opts","type":"{pitch: ArgSpec}","optional":false}]},"effects.blurs.vignetteOverlay":{"id":"effects.blurs.vignetteOverlay","name":"vignetteOverlay","qualifiedName":"vignetteOverlay","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"Darkens the child toward its corners.","description":"`radius` (0–1) is how far the darkening reaches inward, 0 for the edges only and 1 for all the way to the centre. `intensity` (0–1) blends it in. Round on any canvas shape. Works on the three-channel color of a stack begun with `rgbSplit`.","examples":["vignetteOverlay({radius: p('vignetteRadius'), intensity: p('vignetteIntensity')})"],"tips":[],"see":["effects.blurs.scanlines","effects.blurs.opaque"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.vignetteOverlay"},"usedBy":["CRTScreen"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":285},"kind":"function","signature":"vignetteOverlay(opts: {radius: ArgSpec; intensity: ArgSpec}): OverlayStage","params":[{"name":"opts","type":"{radius: ArgSpec; intensity: ArgSpec}","optional":false}]},"effects.blurs.opaque":{"id":"effects.blurs.opaque","name":"opaque","qualifiedName":"opaque","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"Closes a three-channel stack as a solid color with alpha 1.","description":"Put it last after `rgbSplit`, `adjust`, `scanlines`, `phosphorMask` or `vignetteOverlay`, which work on color only and drop the child's alpha.","examples":["effect: gatherStack(rgbSplit({amount: 1}), [scanlines({frequency: 200, intensity: 0.3}), opaque()])"],"tips":[],"see":["effects.blurs.rgbSplit","effects.blurs.gatherStack"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.opaque"},"usedBy":["CRTScreen"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":304},"kind":"function","signature":"opaque(): OverlayStage","params":[]},"effects.blurs.beatPulse":{"id":"effects.blurs.beatPulse","name":"beatPulse","qualifiedName":"beatPulse","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"A faint brightness throb, like the mains hum of an old television.","description":"`wobble` scales the throb (0 turns it off). `speed` scales the global clock. Works on the four-channel color of a stack begun with `chromaSmearTaps`.","examples":["beatPulse({wobble: p('wobble'), speed: p('speed')})"],"tips":[],"see":["effects.blurs.chromaSmearTaps","effects.blurs.tapeWarp"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.beatPulse"},"usedBy":["VHS"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":321},"kind":"function","signature":"beatPulse(opts: {wobble: ArgSpec; speed: ArgSpec}): OverlayStage","params":[{"name":"opts","type":"{wobble: ArgSpec; speed: ArgSpec}","optional":false}]},"effects.blurs.blurPath.linear":{"id":"effects.blurs.blurPath.linear","name":"linear","qualifiedName":"blurPath.linear","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"A straight streak along an angle prop, in degrees (0 points right, 90 points down).","description":"","examples":["effect: motionBlur({path: blurPath.linear(p('angle')), amount: p('intensity')})"],"tips":[],"see":["effects.blurs.motionBlur"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.blurPath.linear"},"usedBy":["LinearBlur"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":360},"kind":"function","signature":"blurPath.linear(angle: PropRef): BlurPathSpec","params":[{"name":"angle","type":"PropRef","optional":false}]},"effects.blurs.blurPath.orbit":{"id":"effects.blurs.blurPath.orbit","name":"orbit","qualifiedName":"blurPath.orbit","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"An arc streak rotating around a center prop (a position in uv).","description":"","examples":["effect: motionBlur({path: blurPath.orbit(p('center')), amount: p('intensity')})"],"tips":[],"see":["effects.blurs.motionBlur"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.blurPath.orbit"},"usedBy":["AngularBlur"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":369},"kind":"function","signature":"blurPath.orbit(center: PropRef): BlurPathSpec","params":[{"name":"center","type":"PropRef","optional":false}]},"effects.blurs.blurPath.zoom":{"id":"effects.blurs.blurPath.zoom","name":"zoom","qualifiedName":"blurPath.zoom","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"A radial streak rushing outward from a center prop (a position in uv).","description":"","examples":["effect: motionBlur({path: blurPath.zoom(p('center')), amount: p('intensity')})"],"tips":[],"see":["effects.blurs.motionBlur"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.blurPath.zoom"},"usedBy":["ZoomBlur"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":378},"kind":"function","signature":"blurPath.zoom(center: PropRef): BlurPathSpec","params":[{"name":"center","type":"PropRef","optional":false}]},"effects.blurs.blurPath":{"id":"effects.blurs.blurPath","name":"blurPath","qualifiedName":"blurPath","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"The three paths `motionBlur` can smear along.","description":"","examples":["effect: motionBlur({path: blurPath.zoom(p('center')), amount: p('intensity')})"],"tips":[],"see":["effects.blurs.motionBlur"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.blurPath"},"usedBy":["AngularBlur","LinearBlur","ZoomBlur"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":351},"kind":"object","signature":"blurPath: { linear, orbit, zoom }","members":["effects.blurs.blurPath.linear","effects.blurs.blurPath.orbit","effects.blurs.blurPath.zoom"],"params":[]},"effects.blurs.motionBlur":{"id":"effects.blurs.motionBlur","name":"motionBlur","qualifiedName":"motionBlur","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"Smears the child along a path: a straight streak, an arc around a point, or a zoom out of a point.","description":"`amount` is a 0–100 intensity. A linear streak spans about twice `amount` in pixels; at 100 an orbit sweeps about 29 degrees and a zoom pulls in from twice the distance to its center. `detail` adds the lens dials (`dispersion`, `falloff`, `focus`) and streak dials (`bias`, `highlights`, `jitter`, and `spiral` for a zoom); all are off at 0 and cost nothing until one is set.","examples":["effect: motionBlur({path: blurPath.linear(p('angle')), amount: p('intensity')})","effect: motionBlur({path: blurPath.zoom(p('center')), amount: p('intensity'), detail: {dispersion: p('dispersion'), falloff: p('falloff'), focus: p('focus'), bias: p('bias'), highlights: p('highlights'), jitter: p('jitter'), spiral: p('spiral')}})"],"tips":["Samples past the canvas edge repeat the border color. Wrap the layer in a larger group if the smear must fade out instead.","Give every `detail` prop `recompile: crosses(0)`; the detail gather is only compiled while one of them is non-zero."],"see":["effects.blurs.blurPath","effects.blurs.gaussianBlur","effects.blurs.scatter"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.motionBlur"},"usedBy":["AngularBlur","LinearBlur","ZoomBlur"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":429},"kind":"function","signature":"motionBlur(opts: {path: BlurPathSpec; amount: ArgSpec; detail?: MotionBlurDetailSlots}): GatherEffect","params":[{"name":"opts","type":"{path: BlurPathSpec; amount: ArgSpec; detail?: MotionBlurDetailSlots}","optional":false}]},"effects.blurs.scatter":{"id":"effects.blurs.scatter","name":"scatter","qualifiedName":"scatter","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"Scatters every pixel of the child by a random offset: a grainy diffusion, not a smooth blur.","description":"`amount` is the largest offset in pixels. `edges` is a prop with `transform: transformEdges` and `compileTime: true` ('stretch' | 'transparent' | 'mirror' | 'wrap') that says what to read where an offset lands outside the canvas. `chromatic` (0–1, optional) scatters red and blue on their own, for a sparkly colour grain; it is off at 0, so give it `recompile: crosses(0)`.","examples":["effect: scatter({amount: p('intensity'), edges: p('edges')})"],"tips":[],"see":["effects.blurs.gaussianBlur","effects.blurs.motionBlur"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.scatter"},"usedBy":["DiffuseBlur"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":491},"kind":"function","signature":"scatter(opts: {amount: ArgSpec; edges: PropRef; chromatic?: PropRef}): GatherEffect","params":[{"name":"opts","type":"{amount: ArgSpec; edges: PropRef; chromatic?: PropRef}","optional":false}]},"effects.blurs.sharpen":{"id":"effects.blurs.sharpen","name":"sharpen","qualifiedName":"sharpen","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"Sharpens the child by boosting the contrast between each pixel and its neighbours.","description":"`amount` 0 leaves the child unchanged; 0–5 is the usual range. Alpha is untouched.","examples":["effect: sharpen(p('sharpness'))"],"tips":["Declare `identityWhen: isZero('sharpness')` and `recompile: crosses(0)` on the prop so 0 costs nothing."],"see":["effects.blurs.gaussianBlur","effects.blurs.scatter"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.sharpen"},"usedBy":["Sharpness"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":521},"kind":"function","signature":"sharpen(amount: ArgSpec): GatherEffect","params":[{"name":"amount","type":"ArgSpec","optional":false}]},"effects.blurs.pixelate":{"id":"effects.blurs.pixelate","name":"pixelate","qualifiedName":"pixelate","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"Turns the child into a grid of flat cells, each showing one color.","description":"`scale` is the number of cells along the canvas's longer edge (1–200; more cells means smaller pixels). `gap` (0–1) is the transparent space between cells as a fraction of a cell. `roundness` (0–1) rounds each cell from a square to a circle.","examples":["effect: pixelate({scale: p('scale'), gap: p('gap'), roundness: p('roundness')})"],"tips":[],"see":["effects.blurs.scatter","effects.blurs.sharpen"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.pixelate"},"usedBy":["Pixelate"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":549},"kind":"function","signature":"pixelate(opts: {scale: ArgSpec; gap: ArgSpec; roundness: ArgSpec}): GatherEffect","params":[{"name":"opts","type":"{scale: ArgSpec; gap: ArgSpec; roundness: ArgSpec}","optional":false}]},"effects.blurs.sphereFrame":{"id":"effects.blurs.sphereFrame","name":"sphereFrame","qualifiedName":"sphereFrame","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"The geometry of a sphere bulging out of the canvas: where each pixel reads the child from, where the sphere ends, and which way its surface faces.","description":"`center` is a position in uv. `radius` 1 is half the canvas height. `depth` is how far it bulges toward the viewer, 0 for flat and up to about 3. Feed the frame to `crispTap` and `rimLit`.","examples":["const sphere = sphereFrame({center: p('center'), radius: p('radius'), depth: p('depth')})"],"tips":[],"see":["effects.blurs.crispTap","effects.blurs.rimLit","GatherFrame"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.sphereFrame"},"usedBy":["Spherize"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":580},"kind":"function","signature":"sphereFrame(opts: {center: ArgSpec; radius: ArgSpec; depth: ArgSpec}): GatherFrame","params":[{"name":"opts","type":"{center: ArgSpec; radius: ArgSpec; depth: ArgSpec}","optional":false}]},"effects.blurs.crispTap":{"id":"effects.blurs.crispTap","name":"crispTap","qualifiedName":"crispTap","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"One clean sample of the child at a frame's warped coordinate.","description":"Use it under a warp that magnifies: hard edges in the child (text, a logo) stay smooth instead of breaking into facets. Returns straight alpha, so close the stack with `{resultAlpha: 'straight'}`.","examples":["effect: gatherStack(crispTap(sphere), [rimLit(sphere, {position: p('light'), intensity: 0.5, softness: 0.5, color: p('lightColor')})], {resultAlpha: 'straight'})"],"tips":[],"see":["effects.blurs.sphereFrame","effects.blurs.rimLit","effects.blurs.childTap"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.crispTap"},"usedBy":["Spherize"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":603},"kind":"function","signature":"crispTap(frame: GatherFrame): SampleStage","params":[{"name":"frame","type":"GatherFrame","optional":false}]},"effects.blurs.rimLit":{"id":"effects.blurs.rimLit","name":"rimLit","qualifiedName":"rimLit","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"A tinted rim of light around the edge of a bulged surface, added on top of the child's color.","description":"`position` is where the light sits, in uv. `intensity` (0–1) is its strength, 0 for off. `softness` (0–1) runs from a hard edge to a soft glow. `color` is a color prop. Outside the frame's surface the result is transparent.","examples":["rimLit(sphere, {position: p('lightPosition'), intensity: p('lightIntensity'), softness: p('lightSoftness'), color: p('lightColor')})"],"tips":[],"see":["effects.blurs.sphereFrame","effects.blurs.crispTap","effects.blurs.fluteHighlight"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.rimLit"},"usedBy":["Spherize"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":626},"kind":"function","signature":"rimLit(frame: GatherFrame, opts: {\n    position: ArgSpec\n    intensity: ArgSpec\n    softness: ArgSpec\n    color: PropRef\n}): OverlayStage","params":[{"name":"frame","type":"GatherFrame","optional":false},{"name":"opts","type":"{\n    position: ArgSpec\n    intensity: ArgSpec\n    softness: ArgSpec\n    color: PropRef\n}","optional":false}]},"effects.blurs.glitchFrame":{"id":"effects.blurs.glitchFrame","name":"glitchFrame","qualifiedName":"glitchFrame","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"The shared geometry of a digital glitch: when bursts fire, which horizontal bands jitter, and which blocks shift or flip.","description":"`intensity` (0–1) sets both the strength and how often bursts happen. `speed` scales the global clock, so no `animatedTime` is needed. `blockDensity` is the base number of bands (2–50). `mirrorAmount` (0–1) is the chance a block shows flipped content. Keep the frame in a module constant and pass it to `glitchRgbSplit`, `colorBarFills` and `distortedScanlines`.","examples":["const glitch = glitchFrame({intensity: p('intensity'), speed: p('speed'), blockDensity: p('blockDensity'), mirrorAmount: p('mirrorAmount')})"],"tips":[],"see":["effects.blurs.glitchRgbSplit","effects.blurs.colorBarFills","effects.blurs.distortedScanlines","GatherFrame"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.glitchFrame"},"usedBy":["Glitch"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":660},"kind":"function","signature":"glitchFrame(opts: {\n    intensity: ArgSpec\n    speed: ArgSpec\n    blockDensity: ArgSpec\n    mirrorAmount: ArgSpec\n}): GatherFrame","params":[{"name":"opts","type":"{\n    intensity: ArgSpec\n    speed: ArgSpec\n    blockDensity: ArgSpec\n    mirrorAmount: ArgSpec\n}","optional":false}]},"effects.blurs.glitchRgbSplit":{"id":"effects.blurs.glitchRgbSplit","name":"glitchRgbSplit","qualifiedName":"glitchRgbSplit","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"The child sampled through a glitch frame, with red and blue pulled apart inside the active bands.","description":"`shift` is the split distance, 0–20. A transparent child stays transparent through the glitch.","examples":["effect: gatherStack(glitchRgbSplit(glitch, {shift: p('rgbShift')}), [colorBarFills(glitch, {intensity: 0.2})])"],"tips":[],"see":["effects.blurs.glitchFrame","effects.blurs.colorBarFills","effects.blurs.rgbSplit"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.glitchRgbSplit"},"usedBy":["Glitch"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":686},"kind":"function","signature":"glitchRgbSplit(frame: GatherFrame, opts: {shift: ArgSpec}): SampleStage","params":[{"name":"frame","type":"GatherFrame","optional":false},{"name":"opts","type":"{shift: ArgSpec}","optional":false}]},"effects.blurs.colorBarFills":{"id":"effects.blurs.colorBarFills","name":"colorBarFills","qualifiedName":"colorBarFills","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"Neon color bars filling the blocks a glitch frame currently has active.","description":"`intensity` (0–1) is how strongly the bars show. They respect the child's alpha.","examples":["colorBarFills(glitch, {intensity: p('colorBarIntensity')})"],"tips":[],"see":["effects.blurs.glitchFrame","effects.blurs.glitchRgbSplit","effects.blurs.distortedScanlines"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.colorBarFills"},"usedBy":["Glitch"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":713},"kind":"function","signature":"colorBarFills(frame: GatherFrame, opts: {intensity: ArgSpec}): OverlayStage","params":[{"name":"frame","type":"GatherFrame","optional":false},{"name":"opts","type":"{intensity: ArgSpec}","optional":false}]},"effects.blurs.distortedScanlines":{"id":"effects.blurs.distortedScanlines","name":"distortedScanlines","qualifiedName":"distortedScanlines","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"CRT-style scanlines that appear only where a glitch frame is currently distorting.","description":"`intensity` (0–1) is how visible they are.","examples":["distortedScanlines(glitch, {intensity: p('scanlineIntensity')})"],"tips":[],"see":["effects.blurs.glitchFrame","effects.blurs.scanlines"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.distortedScanlines"},"usedBy":["Glitch"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":735},"kind":"function","signature":"distortedScanlines(frame: GatherFrame, opts: {intensity: ArgSpec}): OverlayStage","params":[{"name":"frame","type":"GatherFrame","optional":false},{"name":"opts","type":"{intensity: ArgSpec}","optional":false}]},"effects.blurs.gaussianBlur":{"id":"effects.blurs.gaussianBlur","name":"gaussianBlur","qualifiedName":"gaussianBlur","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"A soft, even blur of the child.","description":"`intensity` is a prop from 0 to about 200; 100 is a blur radius of about 36 pixels. Bind the prop to a map to vary the blur across the canvas. Only color blurs; the child's alpha stays sharp, so soft edges need a group or mask around the layer.","examples":["...gaussianBlur({intensity: p('intensity')})"],"tips":["The blur runs at a fixed working resolution, so its cost does not grow with the canvas."],"see":["effects.blurs.channelBlur","effects.blurs.progressiveBlur","effects.blurs.tiltShift","effects.blurs.bloom","effects.blurs.motionBlur"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.gaussianBlur"},"usedBy":["Blur"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":785},"kind":"function","signature":"gaussianBlur(opts: {intensity: PropRef}): ComputeBackedEffect","params":[{"name":"opts","type":"{intensity: PropRef}","optional":false}]},"effects.blurs.channelBlur":{"id":"effects.blurs.channelBlur","name":"channelBlur","qualifiedName":"channelBlur","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"Blurs red, green and blue by separate amounts, for a soft chromatic fringe.","description":"Each of `red`, `green` and `blue` is a prop from 0 to 100; 100 is a radius of about 10 pixels. A channel at 0 stays exactly sharp.","examples":["...channelBlur({red: p('redIntensity'), green: p('greenIntensity'), blue: p('blueIntensity')})"],"tips":[],"see":["effects.blurs.gaussianBlur","effects.blurs.rgbSplit"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.channelBlur"},"usedBy":["ChannelBlur"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":835},"kind":"function","signature":"channelBlur(opts: {red: PropRef; green: PropRef; blue: PropRef}): ComputeBackedEffect","params":[{"name":"opts","type":"{red: PropRef; green: PropRef; blue: PropRef}","optional":false}]},"effects.blurs.progressiveBlur":{"id":"effects.blurs.progressiveBlur","name":"progressiveBlur","qualifiedName":"progressiveBlur","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"A blur that ramps from sharp to full strength in one direction across the child.","description":"The ramp starts at `center` (a position in uv) and runs along `angle` (degrees, 0 points right), reaching full blur `falloff` (0–1, in uv) past the center. `intensity` (0–100) is the blur at the far end; 100 is a radius of about 36 pixels. Bind `intensity` to a map to vary the ceiling per pixel. Alpha stays sharp. `detail` adds the lens dials (see `VariableBlurDetailSlots`), both off at 0.","examples":["...progressiveBlur({intensity: p('intensity'), angle: p('angle'), center: p('center'), falloff: p('falloff')})"],"tips":[],"see":["effects.blurs.tiltShift","effects.blurs.gaussianBlur"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.progressiveBlur"},"usedBy":["ProgressiveBlur"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":904},"kind":"function","signature":"progressiveBlur(opts: {\n    intensity: PropRef\n    angle: PropRef\n    center: PropRef\n    falloff: PropRef\n    detail?: VariableBlurDetailSlots\n}): ComputeBackedEffect","params":[{"name":"opts","type":"{\n    intensity: PropRef\n    angle: PropRef\n    center: PropRef\n    falloff: PropRef\n    detail?: VariableBlurDetailSlots\n}","optional":false}]},"effects.blurs.tiltShift":{"id":"effects.blurs.tiltShift","name":"tiltShift","qualifiedName":"tiltShift","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"Keeps a band of the child in focus and blurs everything beyond it, like a tilt-shift lens.","description":"The band runs through `center` (a position in uv) at `angle` (degrees, 0 is horizontal) and is `width` wide (0–1, in uv). Beyond it the blur ramps to full strength over `falloff` (0–1). `intensity` (0–100) is the blur at the far edges. Pixels inside the band stay pixel-sharp. `detail` adds the lens dials (see `VariableBlurDetailSlots`), both off at 0.","examples":["...tiltShift({intensity: p('intensity'), width: p('width'), falloff: p('falloff'), angle: p('angle'), center: p('center')})"],"tips":[],"see":["effects.blurs.progressiveBlur","effects.blurs.gaussianBlur","effects.blurs.bokehDefocus"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.tiltShift"},"usedBy":["TiltShift"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":983},"kind":"function","signature":"tiltShift(opts: {\n    intensity: PropRef\n    width: PropRef\n    falloff: PropRef\n    angle: PropRef\n    center: PropRef\n    detail?: VariableBlurDetailSlots\n}): ComputeBackedEffect","params":[{"name":"opts","type":"{\n    intensity: PropRef\n    width: PropRef\n    falloff: PropRef\n    angle: PropRef\n    center: PropRef\n    detail?: VariableBlurDetailSlots\n}","optional":false}]},"effects.blurs.bloom":{"id":"effects.blurs.bloom","name":"bloom","qualifiedName":"bloom","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"A glow: the child's bright parts spread into a soft halo that adds back on top.","description":"Pixels brighter than `threshold` (0–1) glow. `size` is the halo's spread in pixels (clean up to about 72). `intensity` (0–50) is how bright the halo is. The halo reaches past the child's alpha, so a glowing shape lights up transparent space around it. At size 0 nothing runs; give the size prop `recompile: crosses(0)`.","examples":["...bloom({intensity: p('intensity'), threshold: p('threshold'), size: p('size')})"],"tips":[],"see":["effects.blurs.screenedBloom","effects.blurs.gaussianBlur","effects.blurs.bokehDefocus"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.bloom"},"usedBy":["Glow"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":1069},"kind":"function","signature":"bloom(opts: {intensity: PropRef; threshold: PropRef; size: PropRef}): ComputeBackedEffect","params":[{"name":"opts","type":"{intensity: PropRef; threshold: PropRef; size: PropRef}","optional":false}]},"effects.blurs.childTap":{"id":"effects.blurs.childTap","name":"childTap","qualifiedName":"childTap","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"The child's own color at this pixel, unchanged.","description":"The sampling stage for an effect that only adds something around the child, like a shadow.","examples":["effect: gatherStack(childTap(), [shadowComposite({coverage, color: p('color'), intensity: p('intensity'), cutout: p('cutout')})])"],"tips":[],"see":["effects.blurs.shadowComposite","effects.blurs.crispTap"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.childTap"},"usedBy":["DropShadow"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":1107},"kind":"function","signature":"childTap(): SampleStage","params":[]},"effects.blurs.shadowOffset":{"id":"effects.blurs.shadowOffset","name":"shadowOffset","qualifiedName":"shadowOffset","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"The coordinate a shadow reads the child's silhouette from: `distance` away in the compass direction `angle`.","description":"`angle` is in degrees, 0 up, 90 right, 180 down. `distance` (0–1) is a fraction of the canvas height and covers the same number of pixels in either axis. Pass the result as the `at` of `silhouetteCoverage`.","examples":["silhouetteCoverage({at: shadowOffset({angle: p('angle'), distance: p('distance')}), blur: p('blur')})"],"tips":[],"see":["effects.blurs.silhouetteCoverage","effects.blurs.shadowComposite"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.shadowOffset"},"usedBy":["DropShadow"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":1125},"kind":"function","signature":"shadowOffset(opts: {angle: ArgSpec; distance: ArgSpec}): (params: RttFilterParams) => Expr","params":[{"name":"opts","type":"{angle: ArgSpec; distance: ArgSpec}","optional":false}]},"effects.blurs.silhouetteCoverage":{"id":"effects.blurs.silhouetteCoverage","name":"silhouetteCoverage","qualifiedName":"silhouetteCoverage","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"A soft copy of the child's alpha silhouette: a number per pixel from 0 to 1.","description":"`at` is where to read the silhouette from, usually `shadowOffset`. `blur` is the softness as a radius in pixels (0–20). Feed it to `shadowComposite` as the `coverage`.","examples":["silhouetteCoverage({at: shadowOffset({angle: 135, distance: 0.1}), blur: p('blur')})"],"tips":[],"see":["effects.blurs.shadowOffset","effects.blurs.shadowComposite"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.silhouetteCoverage"},"usedBy":["DropShadow"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":1146},"kind":"function","signature":"silhouetteCoverage(opts: {at: (params: RttFilterParams) => Expr; blur: ArgSpec}): (params: RttFilterParams) => Expr","params":[{"name":"opts","type":"{at: (params: RttFilterParams) => Expr; blur: ArgSpec}","optional":false}]},"effects.blurs.shadowComposite":{"id":"effects.blurs.shadowComposite","name":"shadowComposite","qualifiedName":"shadowComposite","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"A tinted shadow behind the child.","description":"`coverage` is the soft silhouette from `silhouetteCoverage`, tinted with `color` (a color prop) at `intensity` (0–1) and placed under the child. `cutout` is a boolean prop with `compileTime: true`; when true only the shadow shows, with the child's shape punched out of it.","examples":["effect: gatherStack(childTap(), [shadowComposite({coverage: silhouetteCoverage({at: shadowOffset({angle: p('angle'), distance: p('distance')}), blur: p('blur')}), color: p('color'), intensity: p('intensity'), cutout: p('cutout')})])"],"tips":[],"see":["effects.blurs.childTap","effects.blurs.shadowOffset","effects.blurs.silhouetteCoverage"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.shadowComposite"},"usedBy":["DropShadow"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":1171},"kind":"function","signature":"shadowComposite(opts: {\n    coverage: (params: RttFilterParams) => Expr\n    color: PropRef\n    intensity: ArgSpec\n    cutout: PropRef\n}): OverlayStage","params":[{"name":"opts","type":"{\n    coverage: (params: RttFilterParams) => Expr\n    color: PropRef\n    intensity: ArgSpec\n    cutout: PropRef\n}","optional":false}]},"effects.blurs.fluteFrame":{"id":"effects.blurs.fluteFrame","name":"fluteFrame","qualifiedName":"fluteFrame","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"The geometry of fluted (reeded) glass over the child: where each pixel reads through the glass, how far its colors split, and the slope of the flute at that point.","description":"`shape` is a `compileTime` prop whose transform maps 'bars', 'rounded' and 'waves' to 0, 1 and 2. `angle` is in degrees, 0 for vertical flutes. `frequency` is the number of flutes across the longer edge (1–20). `softness` (0–1) eases each flute from flat-with-sharp-seams to a gentle curve. `waveAmplitude` and `waveFrequency` shape the waves variant only. `refraction` (0–4) is how hard each flute bends the child. `aberration` (0–1) is the color split at the seams. The pattern drifts on the layer's clock, so declare `animatedTime: {speed: 'speed'}`. Keep the frame in a module constant and pass it to `refractedTaps` and `fluteHighlight`.","examples":["const flute = fluteFrame({shape: p('shape'), angle: p('angle'), frequency: p('frequency'), softness: p('softness'), waveAmplitude: p('waveAmplitude'), waveFrequency: p('waveFrequency'), refraction: p('refraction'), aberration: p('aberration')})"],"tips":[],"see":["effects.blurs.refractedTaps","effects.blurs.fluteHighlight","GatherFrame"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.fluteFrame"},"usedBy":["FlutedGlass"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":1217},"kind":"function","signature":"fluteFrame(opts: {\n    shape: PropRef\n    angle: ArgSpec\n    frequency: ArgSpec\n    softness: ArgSpec\n    waveAmplitude: ArgSpec\n    waveFrequency: ArgSpec\n    refraction: ArgSpec\n    aberration: ArgSpec\n}): GatherFrame","params":[{"name":"opts","type":"{\n    shape: PropRef\n    angle: ArgSpec\n    frequency: ArgSpec\n    softness: ArgSpec\n    waveAmplitude: ArgSpec\n    waveFrequency: ArgSpec\n    refraction: ArgSpec\n    aberration: ArgSpec\n}","optional":false}]},"effects.blurs.refractedTaps":{"id":"effects.blurs.refractedTaps","name":"refractedTaps","qualifiedName":"refractedTaps","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"The child seen through a flute frame, with edge handling and a color split when `aberration` is above 0.","description":"`edges` is a prop with `transform: transformEdges` and `compileTime: true`. `aberration` is the same prop the frame uses; give it a recompile rule that fires when it crosses 0. Returns straight alpha, so close the stack with `{resultAlpha: 'straight'}`.","examples":["effect: gatherStack(refractedTaps(flute, {aberration: p('aberration'), edges: p('edges')}), [fluteHighlight(flute, {lightAngle: 30, highlight: 0.2, softness: 0.3, color: p('highlightColor')})], {resultAlpha: 'straight'})"],"tips":[],"see":["effects.blurs.fluteFrame","effects.blurs.fluteHighlight","effects.blurs.crispTap"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.refractedTaps"},"usedBy":["FlutedGlass"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":1260},"kind":"function","signature":"refractedTaps(frame: GatherFrame, opts: {aberration: PropRef; edges: PropRef}): SampleStage","params":[{"name":"frame","type":"GatherFrame","optional":false},{"name":"opts","type":"{aberration: PropRef; edges: PropRef}","optional":false}]},"effects.blurs.fluteHighlight":{"id":"effects.blurs.fluteHighlight","name":"fluteHighlight","qualifiedName":"fluteHighlight","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"A specular highlight running along each flute, added on top of the child's color.","description":"`lightAngle` is the light's direction in degrees, 0 head-on and 90 grazing (−90 to 90). `highlight` (0–2) is its strength. `softness` (0–1) spreads the peak from pin-tight to a broad sheen. `color` is a color prop. Transparent parts of the child pick up no highlight.","examples":["fluteHighlight(flute, {lightAngle: p('lightAngle'), highlight: p('highlight'), softness: p('highlightSoftness'), color: p('highlightColor')})"],"tips":[],"see":["effects.blurs.fluteFrame","effects.blurs.refractedTaps","effects.blurs.rimLit"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.fluteHighlight"},"usedBy":["FlutedGlass"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":1290},"kind":"function","signature":"fluteHighlight(frame: GatherFrame, opts: {\n    lightAngle: ArgSpec\n    highlight: ArgSpec\n    softness: ArgSpec\n    color: PropRef\n}): OverlayStage","params":[{"name":"frame","type":"GatherFrame","optional":false},{"name":"opts","type":"{\n    lightAngle: ArgSpec\n    highlight: ArgSpec\n    softness: ArgSpec\n    color: PropRef\n}","optional":false}]},"effects.blurs.peelFrame":{"id":"effects.blurs.peelFrame","name":"peelFrame","qualifiedName":"peelFrame","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"The geometry of a page curling up from one corner: where the curl reads the child from, how far it has turned, and how far each pixel is from the crease.","description":"`corner` is a `compileTime` string prop ('top-left' | 'top-right' | 'bottom-left' | 'bottom-right') with no transform. `amount` (0–1) is how far the peel has progressed, 0 flat and 1 fully peeled. `radius` is the tightness of the curl as a fraction of the page diagonal (0.02–0.4). Keep the frame in a module constant and pass it to the peel shading words and `peelCompose`.","examples":["const peel = peelFrame({corner: p('corner'), amount: p('amount'), radius: p('radius')})"],"tips":["Leave the `corner` prop without a `transform`: it must stay a string for the frame to read."],"see":["effects.blurs.peelCompose","effects.blurs.curlShading","effects.blurs.curlSheen","effects.blurs.overhangShadow","effects.blurs.revealShadow"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.peelFrame"},"usedBy":["PagePeel"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":1335},"kind":"function","signature":"peelFrame(opts: {corner: PropRef; amount: ArgSpec; radius: ArgSpec}): GatherFrame","params":[{"name":"opts","type":"{corner: PropRef; amount: ArgSpec; radius: ArgSpec}","optional":false}]},"effects.blurs.curlShading":{"id":"effects.blurs.curlShading","name":"curlShading","qualifiedName":"curlShading","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"The darkening in the crook of a curl, easing to full brightness at the lip.","description":"`shading` (0–1) is how deep the darkening goes.","examples":["curlShading(peel, {shading: p('shading')})"],"tips":[],"see":["effects.blurs.peelFrame","effects.blurs.peelCompose","effects.blurs.curlSheen"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.curlShading"},"usedBy":["PagePeel"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":1358},"kind":"function","signature":"curlShading(frame: GatherFrame, opts: {shading: ArgSpec}): PeelShade","params":[{"name":"frame","type":"GatherFrame","optional":false},{"name":"opts","type":"{shading: ArgSpec}","optional":false}]},"effects.blurs.curlSheen":{"id":"effects.blurs.curlSheen","name":"curlSheen","qualifiedName":"curlSheen","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"The band of light running up a curl.","description":"`highlight` (0–1) is its strength. `softness` (0–1) runs from a tight gloss to a broad satin.","examples":["curlSheen(peel, {highlight: p('highlight'), softness: p('highlightSoftness')})"],"tips":[],"see":["effects.blurs.peelFrame","effects.blurs.peelCompose","effects.blurs.curlShading"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.curlSheen"},"usedBy":["PagePeel"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":1374},"kind":"function","signature":"curlSheen(frame: GatherFrame, opts: {highlight: ArgSpec; softness: ArgSpec}): PeelShade","params":[{"name":"frame","type":"GatherFrame","optional":false},{"name":"opts","type":"{highlight: ArgSpec; softness: ArgSpec}","optional":false}]},"effects.blurs.overhangShadow":{"id":"effects.blurs.overhangShadow","name":"overhangShadow","qualifiedName":"overhangShadow","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"The shadow a lifted curl casts on the flat page just past the crease.","description":"`amount` is the same peel amount the frame uses. `shadow` (0–1) is the shadow's strength.","examples":["overhangShadow(peel, {amount: p('amount'), shadow: p('shadow')})"],"tips":[],"see":["effects.blurs.peelFrame","effects.blurs.peelCompose","effects.blurs.revealShadow"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.overhangShadow"},"usedBy":["PagePeel"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":1392},"kind":"function","signature":"overhangShadow(frame: GatherFrame, opts: {amount: ArgSpec; shadow: ArgSpec}): PeelShade","params":[{"name":"frame","type":"GatherFrame","optional":false},{"name":"opts","type":"{amount: ArgSpec; shadow: ArgSpec}","optional":false}]},"effects.blurs.revealShadow":{"id":"effects.blurs.revealShadow","name":"revealShadow","qualifiedName":"revealShadow","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"The shadow on the surface a peel reveals, just inside the crease.","description":"`amount` is the same peel amount the frame uses. `shadow` (0–1) is the shadow's strength.","examples":["revealShadow(peel, {amount: p('amount'), shadow: p('shadow')})"],"tips":[],"see":["effects.blurs.peelFrame","effects.blurs.peelCompose","effects.blurs.overhangShadow"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.revealShadow"},"usedBy":["PagePeel"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":1413},"kind":"function","signature":"revealShadow(frame: GatherFrame, opts: {amount: ArgSpec; shadow: ArgSpec}): PeelShade","params":[{"name":"frame","type":"GatherFrame","optional":false},{"name":"opts","type":"{amount: ArgSpec; shadow: ArgSpec}","optional":false}]},"effects.blurs.peelCompose":{"id":"effects.blurs.peelCompose","name":"peelCompose","qualifiedName":"peelCompose","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"A finished page peel: the cast shadows, the flat page and the lit curl, layered back to front.","description":"The sampling stage of a peel. Pass the four shading terms built on the same frame. Wrap it in `gatherStack` with no overlays.","examples":["effect: gatherStack(peelCompose(peel, {shade: curlShading(peel, {shading: 0.55}), sheen: curlSheen(peel, {highlight: 0.4, softness: 0.2}), overhang: overhangShadow(peel, {amount: p('amount'), shadow: 1}), reveal: revealShadow(peel, {amount: p('amount'), shadow: 1})}), [])"],"tips":[],"see":["effects.blurs.peelFrame","effects.blurs.curlShading","effects.blurs.curlSheen","effects.blurs.overhangShadow","effects.blurs.revealShadow"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.peelCompose"},"usedBy":["PagePeel"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":1437},"kind":"function","signature":"peelCompose(frame: GatherFrame, parts: {\n    shade: PeelShade\n    sheen: PeelShade\n    overhang: PeelShade\n    reveal: PeelShade\n}): SampleStage","params":[{"name":"frame","type":"GatherFrame","optional":false},{"name":"parts","type":"{\n    shade: PeelShade\n    sheen: PeelShade\n    overhang: PeelShade\n    reveal: PeelShade\n}","optional":false}]},"effects.blurs.mirrorAcrossRow":{"id":"effects.blurs.mirrorAcrossRow","name":"mirrorAcrossRow","qualifiedName":"mirrorAcrossRow","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"The coordinate mirrored across a horizontal line, for reading a reflection below it.","description":"`line` is a prop, 0–1 down the canvas in uv. Returns a function of the fragment params that gives the mirrored uv. Above the line the coordinate is still mirrored; `planarReflection` only shows the reflection below it.","examples":["const reflectedUV = mirrorAcrossRow(p('height'))"],"tips":[],"see":["effects.blurs.planarReflection","effects.blurs.depthRampBlur"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.mirrorAcrossRow"},"usedBy":["ReflectivePlane"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":1545},"kind":"function","signature":"mirrorAcrossRow(line: PropRef): (params: GpuFragmentParams) => Expr","params":[{"name":"line","type":"PropRef","optional":false}]},"effects.blurs.depthRampBlur":{"id":"effects.blurs.depthRampBlur","name":"depthRampBlur","qualifiedName":"depthRampBlur","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"The blur half of a mirror floor: blurs the child more the further a row sits below the line.","description":"`line` is the same 0–1 line prop as `mirrorAcrossRow`. `blur` is the blur far below the line, 0–5 where 1 is a radius of about 12 pixels. `blurDistance` (in uv, 0.01–1) is how far below the line the blur takes to reach full strength. `halfKernel` is the number of taps each side per pass; 30 is a good value. Spread it into a definition with `species: 'custom'` and read the result in the fragment as `computeOutputs.blurredTexture`.","examples":["...depthRampBlur({line: p('height'), blur: p('blur'), blurDistance: p('blurDistance'), halfKernel: 30})"],"tips":[],"see":["effects.blurs.planarReflection","effects.blurs.mirrorAcrossRow","effects.blurs.progressiveBlur"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.depthRampBlur"},"usedBy":["ReflectivePlane"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":1567},"kind":"function","signature":"depthRampBlur(opts: {line: PropRef; blur: PropRef; blurDistance: PropRef; halfKernel: number}): {compute: GpuComputeNode}","params":[{"name":"opts","type":"{line: PropRef; blur: PropRef; blurDistance: PropRef; halfKernel: number}","optional":false}]},"effects.blurs.planarReflection":{"id":"effects.blurs.planarReflection","name":"planarReflection","qualifiedName":"planarReflection","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"A mirror floor: the child as it is above a line, and its reflection fading out below it.","description":"`line` (0–1 in uv), `distance` (how far below the line the reflection stays visible, in uv) and `falloff` (the width of the fade as a fraction of `distance`) are props. `edges` is a prop with `transform: transformEdges` and `compileTime: true`. `original` is the child's color at this pixel, `reflection` samples the child (or its blurred copy) at a coordinate, and `reflectedUV` is usually `mirrorAcrossRow(line)`. Returns a fragment function whose result is premultiplied; unpremultiply it before returning from `gpu.fragment`.","examples":["planarReflection({line: p('height'), distance: p('distance'), falloff: p('falloff'), edges: p('edges')}, source.sample(ctx.uv), (uv) => blurred.sample(uv), mirrorAcrossRow(p('height')))(params)"],"tips":[],"see":["effects.blurs.mirrorAcrossRow","effects.blurs.depthRampBlur"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.planarReflection"},"usedBy":["ReflectivePlane"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":1598},"kind":"function","signature":"planarReflection(opts: {line: PropRef; distance: PropRef; falloff: PropRef; edges: PropRef}, original: Expr, reflection: (uv: Expr) => Expr, reflectedUV: (params: GpuFragmentParams) => Expr): (params: GpuFragmentParams) => Expr","params":[{"name":"opts","type":"{line: PropRef; distance: PropRef; falloff: PropRef; edges: PropRef}","optional":false},{"name":"original","type":"Expr","optional":false},{"name":"reflection","type":"(uv: Expr) => Expr","optional":false},{"name":"reflectedUV","type":"(params: GpuFragmentParams) => Expr","optional":false}]},"effects.blurs.screenedBloom":{"id":"effects.blurs.screenedBloom","name":"screenedBloom","qualifiedName":"screenedBloom","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"A tinted glow of the child's bright parts, screened over a color you have already graded: film halation, phosphor bloom.","description":"`strength` is a prop; at 0 the glow pass does not run, so give it `recompile: crosses(0)`. `radius` is a prop for the glow's spread in pixels. `tint` is an rgb triple (0–1) and `threshold` (0–1) the brightness a pixel must pass to glow. `output` and `extractName` are names unique to your definition. Returns `compute`, to set as the definition's `compute:`, and `screen(base, at, params)`, which screens the glow at coordinate `at` over `base` in the fragment. The glow reaches past the child's alpha.","examples":["const halation = screenedBloom({strength: p('halation'), radius: p('halationRadius'), tint: [1, 0.38, 0.16], threshold: 0.62, output: 'halationTexture', extractName: 'myHalationExtract'})"],"tips":["Build it once at module level so the `compute:` field and the fragment read the same part."],"see":["effects.blurs.bloom","effects.blurs.frameSway"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.screenedBloom"},"usedBy":["FilmStock"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":1657},"kind":"function","signature":"screenedBloom(slots: {\n    strength: PropRef\n    radius: PropRef\n    tint: [number, number, number]\n    threshold: number\n    output: string\n    extractName: string\n}): {\n    compute: GpuComputeNode\n    screen: (base: Expr, at: Expr, params: GpuFragmentParams) => Expr\n}","params":[{"name":"slots","type":"{\n    strength: PropRef\n    radius: PropRef\n    tint: [number, number, number]\n    threshold: number\n    output: string\n    extractName: string\n}","optional":false}]},"effects.blurs.frameSway":{"id":"effects.blurs.frameSway","name":"frameSway","qualifiedName":"frameSway","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"A slight drifting shift and rotation of where the child is read from, like film in an unsteady projector gate.","description":"`amount` is a prop from 0 to 1. `field` names an entry you declare in the definition's `extraFields` as `{schema: schema.f32, initial: 0}`; the sway's clock lives there. Returns a function of the fragment params that gives the uv to sample the child at. When `amount` drops to 0 the motion freezes in place rather than snapping back.","examples":["const weave = frameSway({amount: p('weave'), field: 'weaveTime'})"],"tips":["Build it once at module level and call it inside `gpu.fragment`; it registers its own per-frame clock."],"see":["effects.blurs.screenedBloom"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.frameSway"},"usedBy":["FilmStock"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":1728},"kind":"function","signature":"frameSway(slots: {amount: PropRef; field: string}): (params: GpuFragmentParams) => Expr","params":[{"name":"slots","type":"{amount: PropRef; field: string}","optional":false}]},"effects.blurs.bokehDefocus":{"id":"effects.blurs.bokehDefocus","name":"bokehDefocus","qualifiedName":"bokehDefocus","category":"effects.blurs","module":"std/effects/blurs.ts","summary":"A photographic lens blur where bright highlights bloom into aperture-shaped discs.","description":"`radius` (0–100) is the defocus; 100 is a disc about 80 pixels across. `gain` (0–10) is how strongly highlights bloom and `threshold` (0–1) the brightness they must pass. `shape` is a string prop: 'blades' | 'circle' | 'star' | 'heart' | 'flower' | 'cross' | 'ring'. `blades` (0–9) counts blades or points for the blades, star and flower shapes; 0–2 is a round iris. `rotation` is in degrees and `fringe` (0–1) adds color fringing at the disc edges. `catEye` (0–1, optional) clips the discs into cat-eye shapes toward the frame corners, the way a lens barrel vignettes its aperture; 0 is off. Both color and coverage blur, so silhouettes soften like a real lens. Changing the aperture never recompiles. Spread it into a definition with `species: 'custom'`, `requiresRTT: true` and `requiresChild: true`.","examples":["...bokehDefocus({radius: p('radius'), gain: p('highlightGain'), threshold: p('highlightThreshold'), shape: p('bladeShape'), blades: p('bladeCount'), rotation: p('bladeRotation'), fringe: p('chromaticFringe')})"],"tips":[],"see":["effects.blurs.gaussianBlur","effects.blurs.tiltShift","effects.blurs.bloom"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.blurs.bokehDefocus"},"usedBy":["BokehBlur"],"documented":true,"source":{"file":"std/effects/blurs.ts","line":1793},"kind":"function","signature":"bokehDefocus(opts: {\n    radius: PropRef\n    gain: PropRef\n    threshold: PropRef\n    shape: PropRef\n    blades: PropRef\n    rotation: PropRef\n    fringe: PropRef\n    catEye?: PropRef\n}): {compute: GpuComputeNode; gpu: {fragment: (params: GpuFragmentParams) => Expr}}","params":[{"name":"opts","type":"{\n    radius: PropRef\n    gain: PropRef\n    threshold: PropRef\n    shape: PropRef\n    blades: PropRef\n    rotation: PropRef\n    fringe: PropRef\n    catEye?: PropRef\n}","optional":false}]},"effects.stylize.bleedWarp":{"id":"effects.stylize.bleedWarp","name":"bleedWarp","qualifiedName":"bleedWarp","category":"effects.stylize","module":"std/effects/stylize.ts","summary":"A sample position that drifts slowly past hard edges, so paint bleeds instead of stopping.","description":"`amount` scales the drift in logical pixels (about four pixels per unit; 0 turns it off). Build it once and pass the same stage as `at` to both `kuwahara` and `wash`, so the brush and the original sample agree.","examples":["const wet = bleedWarp({amount: p('bleed')})"],"tips":["Give the `amount` prop `recompile: crosses(0)` so turning the bleed off removes the work."],"see":["effects.stylize.kuwahara","effects.stylize.wash"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.stylize.bleedWarp"},"usedBy":["Watercolor"],"documented":true,"source":{"file":"std/effects/stylize.ts","line":66},"kind":"function","signature":"bleedWarp(slots: {amount: PropRef}): GatherStage","params":[{"name":"slots","type":"{amount: PropRef}","optional":false}]},"effects.stylize.kuwahara":{"id":"effects.stylize.kuwahara","name":"kuwahara","qualifiedName":"kuwahara","category":"effects.stylize","module":"std/effects/stylize.ts","summary":"A painter's brush: the flattening that turns fine detail into soft patches of one color.","description":"`radius` is the brush size in logical pixels, 1 to 8, and must be a compile-time prop. `at` is the sample position, normally the same `bleedWarp` stage you hand to `wash`. Returns the brush for `wash`.","examples":["brush: kuwahara({at: wet, radius: p('radius')})"],"tips":["Mark `radius` `compileTime: true`. A hidden prop with a fixed default is the usual choice."],"see":["effects.stylize.wash","effects.stylize.bleedWarp"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.stylize.kuwahara"},"usedBy":["Watercolor"],"documented":true,"source":{"file":"std/effects/stylize.ts","line":98},"kind":"function","signature":"kuwahara(slots: {at: GatherStage; radius: PropRef}): KuwaharaBrush","params":[{"name":"slots","type":"{at: GatherStage; radius: PropRef}","optional":false}]},"effects.stylize.paperGrain":{"id":"effects.stylize.paperGrain","name":"paperGrain","qualifiedName":"paperGrain","category":"effects.stylize","module":"std/effects/stylize.ts","summary":"Fine paper grain for the pigment to settle into, one brightness value per screen pixel.","description":"`amount` runs 0 to 1; at 0 the grain is left out entirely. Returns the stage for the `grain` slot of `wash`. Pass the same prop as `paper` there too.","examples":["grain: paperGrain({amount: p('paper')})"],"tips":["Give the `amount` prop `recompile: crosses(0)`."],"see":["effects.stylize.wash"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.stylize.paperGrain"},"usedBy":["Watercolor"],"documented":true,"source":{"file":"std/effects/stylize.ts","line":143},"kind":"function","signature":"paperGrain(slots: {amount: PropRef}): GatherStage","params":[{"name":"slots","type":"{amount: PropRef}","optional":false}]},"effects.stylize.wash":{"id":"effects.stylize.wash","name":"wash","qualifiedName":"wash","category":"effects.stylize","module":"std/effects/stylize.ts","summary":"The watercolor look: the layer inside flattened into soft patches of color over paper grain.","description":"Closes the recipe and returns the effect for `effect:`. `strength` runs from 0 (the original) to 1 (the full wash). `paper` (0 to 1) sets how much grain shows and how far the result tints toward `paperColor`. `at`, `brush` and `grain` are the stages from `bleedWarp`, `kuwahara` and `paperGrain`.","examples":["effect: wash({at: wet, brush: kuwahara({at: wet, radius: p('radius')}), grain: paperGrain({amount: p('paper')}), paper: p('paper'), paperColor: p('paperColor'), strength: p('strength')})"],"tips":["`strength` at 0 skips the whole effect. Give the prop `recompile: crosses(0)`."],"see":["effects.stylize.kuwahara","effects.stylize.bleedWarp","effects.stylize.paperGrain"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.stylize.wash"},"usedBy":["Watercolor"],"documented":true,"source":{"file":"std/effects/stylize.ts","line":170},"kind":"function","signature":"wash(slots: {\n    at: GatherStage\n    brush: KuwaharaBrush\n    grain: GatherStage\n    paper: PropRef\n    paperColor: PropRef\n    strength: PropRef\n}): GatherEffect","params":[{"name":"slots","type":"{\n    at: GatherStage\n    brush: KuwaharaBrush\n    grain: GatherStage\n    paper: PropRef\n    paperColor: PropRef\n    strength: PropRef\n}","optional":false}]},"effects.stylize.sobelTaps":{"id":"effects.stylize.sobelTaps","name":"sobelTaps","qualifiedName":"sobelTaps","category":"effects.stylize","module":"std/effects/stylize.ts","summary":"Eight brightness samples in a ring around each pixel, for finding edges.","description":"`spacing` is the distance to the ring in device pixels (about 0.5 to 4; thicker strokes at larger values). Returns the ring for `chalkSketch`.","examples":["edges: sobelTaps({spacing: p('edgeThickness')})"],"tips":[],"see":["effects.stylize.chalkSketch"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.stylize.sobelTaps"},"usedBy":["Chalkboard"],"documented":true,"source":{"file":"std/effects/stylize.ts","line":220},"kind":"function","signature":"sobelTaps(slots: {spacing: PropRef}): SobelRing","params":[{"name":"slots","type":"{spacing: PropRef}","optional":false}]},"effects.stylize.chalkSketch":{"id":"effects.stylize.chalkSketch","name":"chalkSketch","qualifiedName":"chalkSketch","category":"effects.stylize","module":"std/effects/stylize.ts","summary":"The chalk-drawing look: edges become chalk strokes and dark areas fill with cross-hatching.","description":"Closes the recipe and returns the effect for `effect:`. `sensitivity` (0 to 1) sets how faint an edge still draws. `hatchScale` is the hatch line spacing in device pixels. `shading` (0 to 1) is how much cross-hatch fills the dark regions and `grain` (0 to 1) how dusty the strokes are. `board` and `chalk` are colors. Alpha follows the layer inside.","examples":["effect: chalkSketch({edges: sobelTaps({spacing: p('edgeThickness')}), sensitivity: p('edgeSensitivity'), hatchScale: p('hatchScale'), shading: p('shading'), grain: p('grain'), board: p('boardColor'), chalk: p('chalkColor')})"],"tips":[],"see":["effects.stylize.sobelTaps","effects.stylize.linework"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.stylize.chalkSketch"},"usedBy":["Chalkboard"],"documented":true,"source":{"file":"std/effects/stylize.ts","line":260},"kind":"function","signature":"chalkSketch(slots: {\n    edges: SobelRing\n    sensitivity: PropRef\n    hatchScale: PropRef\n    shading: PropRef\n    grain: PropRef\n    board: PropRef\n    chalk: PropRef\n}): GatherEffect","params":[{"name":"slots","type":"{\n    edges: SobelRing\n    sensitivity: PropRef\n    hatchScale: PropRef\n    shading: PropRef\n    grain: PropRef\n    board: PropRef\n    chalk: PropRef\n}","optional":false}]},"effects.stylize.charGrid":{"id":"effects.stylize.charGrid","name":"charGrid","qualifiedName":"charGrid","category":"effects.stylize","module":"std/effects/stylize.ts","summary":"The character grid: which cell each pixel falls in, and where it sits inside that cell.","description":"`cellSize` is the cell size in pixels on a 1080-pixel-tall canvas; it scales with the canvas height. `spacing` is how much of the cell the character fills (1 fills it). Build it once and share it with `cellSample`, `glyphFor` and `glyphTint`.","examples":["const grid = charGrid({cellSize: p('cellSize'), spacing: p('spacing')})"],"tips":[],"see":["effects.stylize.cellSample","effects.stylize.glyphFor","effects.stylize.glyphTint"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.stylize.charGrid"},"usedBy":["Ascii"],"documented":true,"source":{"file":"std/effects/stylize.ts","line":303},"kind":"function","signature":"charGrid(slots: {cellSize: PropRef; spacing: PropRef}): FragmentStage","params":[{"name":"slots","type":"{cellSize: PropRef; spacing: PropRef}","optional":false}]},"effects.stylize.cellSample":{"id":"effects.stylize.cellSample","name":"cellSample","qualifiedName":"cellSample","category":"effects.stylize","module":"std/effects/stylize.ts","summary":"The color of the layer inside at the centre of each character cell.","description":"One sample per cell, straight alpha. Feeds `glyphFor` (to pick the character) and `glyphTint` (to color it).","examples":["const cell = cellSample({grid})"],"tips":[],"see":["effects.stylize.charGrid","effects.stylize.glyphFor"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.stylize.cellSample"},"usedBy":["Ascii"],"documented":true,"source":{"file":"std/effects/stylize.ts","line":322},"kind":"function","signature":"cellSample(slots: {grid: FragmentStage}): FragmentStage","params":[{"name":"slots","type":"{grid: FragmentStage}","optional":false}]},"effects.stylize.glyphFor":{"id":"effects.stylize.glyphFor","name":"glyphFor","qualifiedName":"glyphFor","category":"effects.stylize","module":"std/effects/stylize.ts","summary":"The character drawn in each cell, picked by how bright the cell is.","description":"`gamma` bends the brightness curve: below 1 favours the light characters, above 1 the dark ones. `atlas` is your function that draws the character set into a texture and returns it (the Ascii shader shows one). The definition needs three extra fields the atlas fills in: `_charCount`, `_atlasScale` and `_atlasSize`.","examples":["glyph: glyphFor({grid, cell, gamma: p('gamma'), atlas: setupGlyphAtlas})"],"tips":[],"see":["effects.stylize.glyphTint","effects.stylize.cellSample"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.stylize.glyphFor"},"usedBy":["Ascii"],"documented":true,"source":{"file":"std/effects/stylize.ts","line":346},"kind":"function","signature":"glyphFor(slots: {\n    grid: FragmentStage\n    cell: FragmentStage\n    gamma: PropRef\n    atlas: (params: GpuFragmentParams) => KitTexture\n}): FragmentStage","params":[{"name":"slots","type":"{\n    grid: FragmentStage\n    cell: FragmentStage\n    gamma: PropRef\n    atlas: (params: GpuFragmentParams) => KitTexture\n}","optional":false}]},"effects.stylize.glyphTint":{"id":"effects.stylize.glyphTint","name":"glyphTint","qualifiedName":"glyphTint","category":"effects.stylize","module":"std/effects/stylize.ts","summary":"The finished ASCII look: each character tinted with its cell's color, everything else transparent.","description":"Returns the fragment for `gpu.fragment`. Cells whose alpha is below `alphaThreshold` (0 to 1) turn transparent. With `preserveAlpha` on, the output keeps the alpha of the layer inside; off, visible characters are fully opaque.","examples":["gpu: {fragment: glyphTint({grid, cell, glyph, alphaThreshold: p('alphaThreshold'), preserveAlpha: p('preserveAlpha')})}"],"tips":[],"see":["effects.stylize.glyphFor","effects.stylize.charGrid"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.stylize.glyphTint"},"usedBy":["Ascii"],"documented":true,"source":{"file":"std/effects/stylize.ts","line":378},"kind":"function","signature":"glyphTint(slots: {\n    grid: FragmentStage\n    cell: FragmentStage\n    glyph: FragmentStage\n    alphaThreshold: PropRef\n    preserveAlpha: PropRef\n}): FragmentStage","params":[{"name":"slots","type":"{\n    grid: FragmentStage\n    cell: FragmentStage\n    glyph: FragmentStage\n    alphaThreshold: PropRef\n    preserveAlpha: PropRef\n}","optional":false}]},"effects.stylize.linework":{"id":"effects.stylize.linework","name":"linework","qualifiedName":"linework","category":"effects.stylize","module":"std/effects/stylize.ts","summary":"The engraving look: the layer inside redrawn as ink lines that swell where it is dark.","description":"Closes the recipe and returns the effect for `effect:`. `style` is a compile-time select: `'line'` (one set of parallel lines), `'crosshatch'` (more sets appear in the shadows) or `'spiral'` (one line coiling out from `center`, a position prop). `frequency` is how many lines span the canvas height. `angle` is in degrees. `relief` (0 to 2) is how far brightness pushes the lines sideways, `waviness` (0 to 1) how much they meander, and `contrast` (0.25 to 3) the tone curve before inking. `ink` and `paper` are colors.","examples":["effect: linework({style: p('style'), frequency: p('frequency'), angle: p('angle'), center: p('center'), relief: p('relief'), waviness: p('waviness'), contrast: p('contrast'), ink: p('inkColor'), paper: p('paperColor')})"],"tips":["Set `blendWithChildren: false` on the definition. The lines replace the layer rather than sit on it."],"see":["effects.stylize.chalkSketch"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.stylize.linework"},"usedBy":["Engraving"],"documented":true,"source":{"file":"std/effects/stylize.ts","line":552},"kind":"function","signature":"linework(slots: {\n    style: PropRef\n    frequency: PropRef\n    angle: PropRef\n    center: PropRef\n    relief: PropRef\n    waviness: PropRef\n    contrast: PropRef\n    ink: PropRef\n    paper: PropRef\n}): GatherEffect","params":[{"name":"slots","type":"{\n    style: PropRef\n    frequency: PropRef\n    angle: PropRef\n    center: PropRef\n    relief: PropRef\n    waviness: PropRef\n    contrast: PropRef\n    ink: PropRef\n    paper: PropRef\n}","optional":false}]},"effects.lens.chromaticFan":{"id":"effects.lens.chromaticFan","name":"chromaticFan","qualifiedName":"chromaticFan","category":"effects.lens","module":"std/effects/lens.ts","summary":"Split the child into red, green and blue copies pushed apart along one direction.","description":"`angle` is in degrees (0 pushes along x); give the prop `transform: transformAngle`. `strength` is 0–1, where 1 moves a channel by a tenth of the canvas height. `red`, `green` and `blue` multiply that shift per channel, usually −1, 0 and 1. Returns a sampling stage; wrap it in `gatherStack` to make the effect.","examples":["effect: gatherStack(chromaticFan({strength: p('strength'), angle: p('angle'), red: p('redOffset'), green: p('greenOffset'), blue: p('blueOffset')}), [])"],"tips":["Alpha comes from the green copy, so keep `green` at 0 to hold the layer's outline in place."],"see":["effects.blurs.gatherStack","effects.lens.spectralLens"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.lens.chromaticFan"},"usedBy":["ChromaticAberration"],"documented":true,"source":{"file":"std/effects/lens.ts","line":54},"kind":"function","signature":"chromaticFan(opts: {\n    strength: ArgSpec\n    angle: ArgSpec\n    red: ArgSpec\n    green: ArgSpec\n    blue: ArgSpec\n}): SampleStage","params":[{"name":"opts","type":"{\n    strength: ArgSpec\n    angle: ArgSpec\n    red: ArgSpec\n    green: ArgSpec\n    blue: ArgSpec\n}","optional":false}]},"effects.lens.spectralLens":{"id":"effects.lens.spectralLens","name":"spectralLens","qualifiedName":"spectralLens","category":"effects.lens","module":"std/effects/lens.ts","summary":"Fan the child out into many color-shifted copies through a warped lens, the spectral-lens look.","description":"A whole fragment for a `gpu:` definition; the child is read as a texture. `count` copies spread along `angle` around `center`, `dispersion` colors them across the spectrum, the focus slots fade the split near the center and the edges, `lensBulge` and `lensCircle` warp the whole image, and `swirl`, `noise` and the grain slots scatter it. Every slot is a prop ref; the field list on `SpectralLensSlots` gives units and ranges.","examples":["gpu: {fragment: spectralLens({center: p('center'), spread: p('spread'), bias: p('bias'), angle: p('angle'), perspective: p('perspective'), count: p('count'), dispersion: p('dispersion'), dispersionShift: p('dispersionShift'), dispersionColor: p('dispersionColor'), focusCenter: p('focusCenter'), focusEdges: p('focusEdges'), swirl: p('swirl'), noise: p('noise'), noiseFrequency: p('noiseFrequency'), noiseOffset: p('noiseOffset'), lensBulge: p('lensBulge'), lensCircle: p('lensCircle'), grainMixer: p('grainMixer'), grainOverlay: p('grainOverlay')})}"],"tips":["Every pixel samples the child `count` times, and phones cap `count` at 24. Keep it low on large canvases."],"see":["effects.lens.chromaticFan"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.lens.spectralLens"},"usedBy":["LensDistortion"],"documented":true,"source":{"file":"std/effects/lens.ts","line":137},"kind":"function","signature":"spectralLens(slots: SpectralLensSlots): (params: GpuFragmentParams) => Expr","params":[{"name":"slots","type":"SpectralLensSlots","optional":false}]},"effects.reveal.Coverage":{"id":"effects.reveal.Coverage","name":"Coverage","qualifiedName":"Coverage","category":"effects.reveal","module":"std/effects/reveal.ts","summary":"The order pixels disappear in: a number per pixel from 0 (goes first) to 1 (goes last).","description":"Get one from a `coverage` word, then reshape it with the methods below. Each method returns a new coverage, so they chain. Pass the result to `reveal`.","examples":["coverage: coverage.directional(p('angle')).folded()"],"tips":[],"see":["effects.reveal.coverage","effects.reveal.reveal"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.reveal.Coverage"},"usedBy":[],"documented":true,"source":{"file":"std/effects/reveal.ts","line":41},"kind":"class","signature":"class Coverage","params":[]},"effects.reveal.BandedCoverage":{"id":"effects.reveal.BandedCoverage","name":"BandedCoverage","qualifiedName":"BandedCoverage","category":"effects.reveal","module":"std/effects/reveal.ts","summary":"A coverage cut into bands.","description":"Bands vanish in order, or in a fixed random order after `.shuffled()`.","examples":["coverage: coverage.radial(p('center')).bands(p('rings'))"],"tips":[],"see":["effects.reveal.Coverage"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.reveal.BandedCoverage"},"usedBy":[],"documented":true,"source":{"file":"std/effects/reveal.ts","line":78},"kind":"class","signature":"class BandedCoverage","params":[]},"effects.reveal.CellCoverage":{"id":"effects.reveal.CellCoverage","name":"CellCoverage","qualifiedName":"CellCoverage","category":"effects.reveal","module":"std/effects/reveal.ts","summary":"The canvas cut into a grid of square cells, waiting for you to pick the order the cells go in.","description":"Not a coverage on its own. Call `.diamond()`, `.checker()` or `.shuffled()` to get one.","examples":["coverage: coverage.cells(p('blockSize')).shuffled()"],"tips":[],"see":["effects.reveal.coverage"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.reveal.CellCoverage"},"usedBy":[],"documented":true,"source":{"file":"std/effects/reveal.ts","line":110},"kind":"class","signature":"class CellCoverage","params":[]},"effects.reveal.coverage.directional":{"id":"effects.reveal.coverage.directional","name":"directional","qualifiedName":"coverage.directional","category":"effects.reveal","module":"std/effects/reveal.ts","summary":"A straight edge sweeping across the canvas.","description":"`angleDeg` is in degrees; 0 wipes left to right, 90 top to bottom. The edge reaches both ends of the canvas exactly at whatever angle you give.","examples":["coverage: coverage.directional(p('angle'))"],"tips":[],"see":["effects.reveal.coverage.radial","effects.reveal.coverage.angular"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.reveal.coverage.directional"},"usedBy":["BarnDoors","LinearWipe","RandomBars","VenetianBlinds"],"documented":true,"source":{"file":"std/effects/reveal.ts","line":163},"kind":"function","signature":"coverage.directional(angleDeg: ArgSpec): Coverage","params":[{"name":"angleDeg","type":"ArgSpec","optional":false}]},"effects.reveal.coverage.radial":{"id":"effects.reveal.coverage.radial","name":"radial","qualifiedName":"coverage.radial","category":"effects.reveal","module":"std/effects/reveal.ts","summary":"A circle growing out from `center`, an iris wipe.","description":"`center` is a position prop (`transform: transformPosition`). The circle is sized to the farthest corner, so it clears the whole canvas even from an off-center start.","examples":["coverage: coverage.radial(p('center'))"],"tips":[],"see":["effects.reveal.coverage.directional","effects.reveal.coverage.angular"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.reveal.coverage.radial"},"usedBy":["IrisWipe","RippleWipe"],"documented":true,"source":{"file":"std/effects/reveal.ts","line":180},"kind":"function","signature":"coverage.radial(center: ArgSpec): Coverage","params":[{"name":"center","type":"ArgSpec","optional":false}]},"effects.reveal.coverage.angular":{"id":"effects.reveal.coverage.angular","name":"angular","qualifiedName":"coverage.angular","category":"effects.reveal","module":"std/effects/reveal.ts","summary":"A clock hand sweeping around `center`.","description":"`startDeg` is where the sweep begins, in degrees. `direction` is `'cw'`, `'ccw'` or `'both'` (two hands opening from the start and meeting opposite it), either as a literal or as a select prop declared `compileTime: true`.","examples":["coverage: coverage.angular(p('center'), p('startAngle'), p('direction'))"],"tips":[],"see":["effects.reveal.coverage.radial","effects.reveal.coverage.directional"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.reveal.coverage.angular"},"usedBy":["RadialWipe"],"documented":true,"source":{"file":"std/effects/reveal.ts","line":198},"kind":"function","signature":"coverage.angular(center: ArgSpec, startDeg: ArgSpec, direction: PropRef | AngularDirection): Coverage","params":[{"name":"center","type":"ArgSpec","optional":false},{"name":"startDeg","type":"ArgSpec","optional":false},{"name":"direction","type":"PropRef | AngularDirection","optional":false}]},"effects.reveal.coverage.cells":{"id":"effects.reveal.coverage.cells","name":"cells","qualifiedName":"coverage.cells","category":"effects.reveal","module":"std/effects/reveal.ts","summary":"A grid of square cells `size` wide.","description":"`size` is a fraction of the canvas width (0.01–0.5). Finish with `.diamond()`, `.checker()` or `.shuffled()` to say how the cells go.","examples":["coverage: coverage.cells(p('blockSize')).checker()"],"tips":[],"see":["effects.reveal.coverage.noise"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.reveal.coverage.cells"},"usedBy":["BlockDissolve","CheckerWipe","DiamondWipe"],"documented":true,"source":{"file":"std/effects/reveal.ts","line":224},"kind":"function","signature":"coverage.cells(size: ArgSpec): CellCoverage","params":[{"name":"size","type":"ArgSpec","optional":false}]},"effects.reveal.coverage.noise":{"id":"effects.reveal.coverage.noise","name":"noise","qualifiedName":"coverage.noise","category":"effects.reveal","module":"std/effects/reveal.ts","summary":"Soft blobs eating the image away, the classic film dissolve.","description":"`scale` sets the blob size (0.5–20; higher is smaller blobs). `seed` picks a different pattern. The pattern itself never moves; only `progress` does.","examples":["coverage: coverage.noise(p('scale'), p('seed'))"],"tips":[],"see":["effects.reveal.coverage.cells"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.reveal.coverage.noise"},"usedBy":["NoiseDissolve"],"documented":true,"source":{"file":"std/effects/reveal.ts","line":240},"kind":"function","signature":"coverage.noise(scale: ArgSpec, seed: ArgSpec): Coverage","params":[{"name":"scale","type":"ArgSpec","optional":false},{"name":"seed","type":"ArgSpec","optional":false}]},"effects.reveal.coverage":{"id":"effects.reveal.coverage","name":"coverage","qualifiedName":"coverage","category":"effects.reveal","module":"std/effects/reveal.ts","summary":"The orders a wipe can follow.","description":"Each word returns a `Coverage` for `reveal`.\n\nAll of them read the canvas in uv (0–1 across, y down) and correct for its aspect ratio, so a circle stays round and a diagonal stays at its true angle.","examples":["coverage: coverage.radial(p('center'))"],"tips":[],"see":["effects.reveal.reveal","effects.reveal.Coverage"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.reveal.coverage"},"usedBy":["BarnDoors","BlockDissolve","CheckerWipe","DiamondWipe","IrisWipe","LinearWipe","NoiseDissolve","RadialWipe","RandomBars","RippleWipe","VenetianBlinds"],"documented":true,"source":{"file":"std/effects/reveal.ts","line":150},"kind":"object","signature":"coverage: { directional, radial, angular, cells, noise }","members":["effects.reveal.coverage.directional","effects.reveal.coverage.radial","effects.reveal.coverage.angular","effects.reveal.coverage.cells","effects.reveal.coverage.noise"],"params":[]},"effects.reveal.reveal":{"id":"effects.reveal.reveal","name":"reveal","qualifiedName":"reveal","category":"effects.reveal","module":"std/effects/reveal.ts","summary":"Fade the child away in coverage order as `progress` goes from 0 to 1.","description":"At 0 the child is fully visible, at 1 it is gone, whatever `feather` is. Only the child's alpha changes; its colors stay put. Turning `invert` on reverses the order, so the last pixels go first.","examples":["effect: reveal({coverage: coverage.directional(p('angle')), progress: p('progress'), feather: p('feather'), invert: p('invert')})"],"tips":["Animate `progress` from outside (a prop driven by scroll or a tween); the wipe itself has no clock."],"see":["effects.reveal.coverage","effects.reveal.Coverage"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.reveal.reveal"},"usedBy":["BarnDoors","BlockDissolve","CheckerWipe","DiamondWipe","IrisWipe","LinearWipe","NoiseDissolve","RadialWipe","RandomBars","RippleWipe","VenetianBlinds"],"documented":true,"source":{"file":"std/effects/reveal.ts","line":273},"kind":"function","signature":"reveal(slots: RevealSlots): PointwiseEffect","params":[{"name":"slots","type":"RevealSlots","optional":false}]},"effects.instances.grid":{"id":"effects.instances.grid","name":"grid","qualifiedName":"grid","category":"effects.instances","module":"std/effects/instances.ts","summary":"Copies in rows and columns, filling this layer's bounding box.","description":"Pass it as the recipe's `placement`, or as one member of `byMode`.","examples":["placement: grid({columns: p('columns'), rows: p('rows'), gapX: p('gapX'), gapY: p('gapY'), stagger: p('stagger'), flip: p('flip')})"],"tips":["Mark `flip` `compileTime: true`. Only the chosen mirroring is compiled in."],"see":["effects.instances.radial","effects.instances.line","effects.instances.byMode"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.instances.grid"},"usedBy":["Repeater"],"documented":true,"source":{"file":"std/effects/instances.ts","line":80},"kind":"function","signature":"grid(slots: GridSlots): PlacementSpec","params":[{"name":"slots","type":"GridSlots","optional":false}]},"effects.instances.radial":{"id":"effects.instances.radial","name":"radial","qualifiedName":"radial","category":"effects.instances","module":"std/effects/instances.ts","summary":"Copies around a circle centred on the layer inside.","description":"`sweep` at 360 spaces them evenly all the way round; smaller sweeps make an arc starting at `startAngle`.","examples":["placement: radial({count: p('count'), radius: p('radius'), startAngle: p('startAngle'), sweep: p('sweep'), faceCenter: p('faceCenter')})"],"tips":[],"see":["effects.instances.grid","effects.instances.line","effects.instances.byMode"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.instances.radial"},"usedBy":["Repeater"],"documented":true,"source":{"file":"std/effects/instances.ts","line":93},"kind":"function","signature":"radial(slots: RadialSlots): PlacementSpec","params":[{"name":"slots","type":"RadialSlots","optional":false}]},"effects.instances.line":{"id":"effects.instances.line","name":"line","qualifiedName":"line","category":"effects.instances","module":"std/effects/instances.ts","summary":"Copies in a row, marching away from the layer inside in one direction.","description":"","examples":["placement: line({count: p('count'), direction: p('direction'), spacing: p('spacing')})"],"tips":[],"see":["effects.instances.grid","effects.instances.radial","effects.instances.byMode"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.instances.line"},"usedBy":["Repeater"],"documented":true,"source":{"file":"std/effects/instances.ts","line":103},"kind":"function","signature":"line(slots: LineSlots): PlacementSpec","params":[{"name":"slots","type":"LineSlots","optional":false}]},"effects.instances.byMode":{"id":"effects.instances.byMode","name":"byMode","qualifiedName":"byMode","category":"effects.instances","module":"std/effects/instances.ts","summary":"Lets one select prop switch between a grid, a radial and a linear layout.","description":"`mode` is a compile-time select with the values `'grid'`, `'radial'` and `'linear'`; only the chosen layout is compiled in.","examples":["placement: byMode(p('mode'), {grid: grid({...}), radial: radial({...}), linear: line({...})})"],"tips":[],"see":["effects.instances.grid","effects.instances.radial","effects.instances.line"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.instances.byMode"},"usedBy":["Repeater"],"documented":true,"source":{"file":"std/effects/instances.ts","line":124},"kind":"function","signature":"byMode(mode: PropRef, members: PlacementPick['members']): PlacementPick","params":[{"name":"mode","type":"PropRef","optional":false},{"name":"members","type":"PlacementPick['members']","optional":false}]},"effects.instances.repeatInstances":{"id":"effects.instances.repeatInstances","name":"repeatInstances","qualifiedName":"repeatInstances","category":"effects.instances","module":"std/effects/instances.ts","summary":"Many copies of the layer inside, laid out and varied as the recipe says.","description":"Returns the fragment for `gpu.fragment` on a definition with `requiresRTT`, `requiresChild` and `wantsBoundsParams` set. Copies stack with normal alpha in the recipe's `order`. The result is straight alpha.","examples":["gpu: {fragment: repeatInstances({source: {crop: {left: p('cropLeft'), right: p('cropRight'), top: p('cropTop'), bottom: p('cropBottom')}}, placement: radial({count: p('count'), radius: p('radius'), startAngle: p('startAngle'), sweep: p('sweep'), faceCenter: p('faceCenter')}), variation: {scale: p('instanceScale'), rotation: p('instanceRotation'), opacity: p('instanceOpacity'), jitter: {position: p('jitterPosition'), rotation: p('jitterRotation'), scale: p('jitterScale'), opacity: p('jitterOpacity')}, seed: p('seed'), phase: p('phase')}, hueShift: p('hueShift'), order: p('zOrder')})}"],"tips":["`hueShift` costs nothing at 0. Give the prop `recompile: crosses(0)` so it switches on cleanly."],"see":["effects.instances.grid","effects.instances.radial","effects.instances.line","effects.instances.byMode"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.instances.repeatInstances"},"usedBy":["Repeater"],"documented":true,"source":{"file":"std/effects/instances.ts","line":178},"kind":"function","signature":"repeatInstances(recipe: RepeatInstancesRecipe): (params: GpuFragmentParams) => Expr","params":[{"name":"recipe","type":"RepeatInstancesRecipe","optional":false}]},"effects.fracture.cellLaneUVFor":{"id":"effects.fracture.cellLaneUVFor","name":"cellLaneUVFor","qualifiedName":"cellLaneUVFor","category":"effects.fracture","module":"std/effects/fracture.ts","summary":"Where one shard's value sits in a one-row data texture.","description":"Lay the texture out as four values per shard, so `laneWidth` is the shard count times four. The returned GPU function takes a shard index and a value index (0 to 3) and gives the uv to sample with `'nearestClamp'`. Built once per width.","examples":["const cellLaneUV = cellLaneUVFor(16 * 4)\nconst dataAt = (idx: Expr, field: number) => dataKit.sample(call(cellLaneUV, 'cellLaneUV', [idx, floatE(field)]), 'nearestClamp').member('r')"],"tips":[],"see":["effects.fracture.voronoiRegionField","effects.fracture.crackGeom"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.fracture.cellLaneUVFor"},"usedBy":["Shatter"],"documented":true,"source":{"file":"std/effects/fracture.ts","line":103},"kind":"function","signature":"cellLaneUVFor(laneWidth: number): TgpuFn<(idx: F32, field: F32) => Vec2f>","params":[{"name":"laneWidth","type":"number","optional":false}]},"effects.fracture.crackGeom":{"id":"effects.fracture.crackGeom","name":"crackGeom","qualifiedName":"crackGeom","category":"effects.fracture","module":"std/effects/fracture.ts","summary":"How much crack there is at a pixel, and which way the shard under it has moved.","description":"Takes the pixel's uv, the nearest and second-nearest shard positions (x and y, in uv), the nearest shard's displacement (x and y, in uv) and `crackWidth` (about 0.5 to 5). Returns a record with `crackIntensity` (0 to 1, and 0 until the shard has actually moved), `edgeNormal` (the direction across the crack), `displacedUV` (where to sample the layer inside) and `disp` (the displacement again, for `crackShardCompose`).","examples":["const geom = call(crackGeom, 'crackGeom', [ctx.uv, dataAt(nearest, 0), dataAt(nearest, 1), dataAt(second, 0), dataAt(second, 1), dataAt(nearest, 2), dataAt(nearest, 3), uniforms.crackWidth])"],"tips":[],"see":["effects.fracture.crackRefractUV","effects.fracture.crackShardCompose","effects.fracture.cellLaneUVFor"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.fracture.crackGeom"},"usedBy":["Shatter"],"documented":true,"source":{"file":"std/effects/fracture.ts","line":139},"kind":"function","signature":"crackGeom(uv: v2f, nPosX: number, nPosY: number, sPosX: number, sPosY: number, dispX: number, dispY: number, crackWidth: number): { crackIntensity: number; edgeNormal: v2f; displacedUV: v2f; disp: v2f; }","params":[{"name":"args","type":"[uv: v2f, nPosX: number, nPosY: number, sPosX: number, sPosY: number, dispX: number, dispY: number, crackWidth: number]","optional":false}]},"effects.fracture.crackRefractUV":{"id":"effects.fracture.crackRefractUV","name":"crackRefractUV","qualifiedName":"crackRefractUV","category":"effects.fracture","module":"std/effects/fracture.ts","summary":"Where to sample one color channel so the crack bends the light and splits it like a prism.","description":"`channel` is 1 for red, 0 for green and -1 for blue; the three samples land slightly apart. `refractionStrength` (0 to 10) bends the sample across the crack and `chromaticSplit` (0 to 5) spreads the channels. Both fade with `crackIntensity`, so unbroken glass is untouched.","examples":["const red = child.sample(call(crackRefractUV, 'crackRefractUV', [geom.member('displacedUV'), geom.member('edgeNormal'), geom.member('crackIntensity'), uniforms.refractionStrength, uniforms.chromaticSplit, floatE(1)]))"],"tips":[],"see":["effects.fracture.crackGeom","effects.fracture.crackShardCompose"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.fracture.crackRefractUV"},"usedBy":["Shatter"],"documented":true,"source":{"file":"std/effects/fracture.ts","line":177},"kind":"function","signature":"crackRefractUV(displacedUV: v2f, edgeNormal: v2f, crackIntensity: number, refractionStrength: number, chromaticSplit: number, channel: number): v2f","params":[{"name":"args","type":"[displacedUV: v2f, edgeNormal: v2f, crackIntensity: number, refractionStrength: number, chromaticSplit: number, channel: number]","optional":false}]},"effects.fracture.crackShardCompose":{"id":"effects.fracture.crackShardCompose","name":"crackShardCompose","qualifiedName":"crackShardCompose","category":"effects.fracture","module":"std/effects/fracture.ts","summary":"The finished glass: the three refracted channels blended into the crack, with a hint of light on tilted shards.","description":"Pass the plain sample at `displacedUV`, the red, green and blue samples from `crackRefractUV`, then `crackIntensity` and `disp` from `crackGeom`, and `shardLighting` (0 to 0.5). The samples come from the layer's texture, so unpremultiply the result before returning it.","examples":["const glass = call(crackShardCompose, 'crackShardCompose', [plain, red, green, blue, geom.member('crackIntensity'), geom.member('disp'), uniforms.shardLighting])"],"tips":[],"see":["effects.fracture.crackRefractUV","effects.fracture.crackGeom"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.fracture.crackShardCompose"},"usedBy":["Shatter"],"documented":true,"source":{"file":"std/effects/fracture.ts","line":209},"kind":"function","signature":"crackShardCompose(normalColor: v4f, rFinal: v4f, gFinal: v4f, bFinal: v4f, crackIntensity: number, disp: v2f, shardLighting: number): v4f","params":[{"name":"args","type":"[normalColor: v4f, rFinal: v4f, gFinal: v4f, bFinal: v4f, crackIntensity: number, disp: v2f, shardLighting: number]","optional":false}]},"effects.fracture.voronoiRegionField":{"id":"effects.fracture.voronoiRegionField","name":"voronoiRegionField","qualifiedName":"voronoiRegionField","category":"effects.fracture","module":"std/effects/fracture.ts","summary":"Splits the canvas into shards around random sites and records, per pixel, its shard and the next-nearest one.","description":"A compute hook for the `compute:` field. `count` is the number of shards, `size` the field's resolution in pixels, `sites(seed)` your function returning the shard positions as `[x, y, x, y, …]` in uv, and `seedProp` the prop that re-rolls them. The field only recomputes when the seed changes. In the fragment, sample `computeOutputs[output]` with `'nearestClamp'`: `.r` is the nearest shard's index, `.g` the second-nearest.","examples":["compute: voronoiRegionField({count: 16, size: 1024, sites: shardSites, seedProp: 'seed', seedDefault: 2, output: 'voronoiField'})"],"tips":["Fill your shard data texture from the same `sites` function, or the cracks drift off their shards."],"see":["effects.fracture.cellLaneUVFor","effects.fracture.crackGeom"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.fracture.voronoiRegionField"},"usedBy":["Shatter"],"documented":true,"source":{"file":"std/effects/fracture.ts","line":247},"kind":"function","signature":"voronoiRegionField(opts: {\n    count: number\n    size: number\n    sites: (seed: number) => Float32Array\n    seedProp: string\n    seedDefault: number\n    output: string\n}): GpuComputeNode","params":[{"name":"opts","type":"{\n    count: number\n    size: number\n    sites: (seed: number) => Float32Array\n    seedProp: string\n    seedDefault: number\n    output: string\n}","optional":false}]},"effects.edgeGlow.heartbeatPulse":{"id":"effects.edgeGlow.heartbeatPulse","name":"heartbeatPulse","qualifiedName":"heartbeatPulse","category":"effects.edgeGlow","module":"std/effects/edgeGlow.ts","summary":"A double heartbeat, 0 to 1, driven by the layer's clock.","description":"Compute it once per pixel and hand the same value to `edgeGlowBand` and every `orbitSpotsAccum` call, so the band's smoke and the spots beat together.","examples":["const beat = asLocal(call(heartbeatPulse, 'heartbeatPulse', [animatedTime(params)]), 'beat')"],"tips":[],"see":["effects.edgeGlow.edgeGlowBand","effects.edgeGlow.orbitSpotsAccum"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.edgeGlow.heartbeatPulse"},"usedBy":["LightEdge"],"documented":true,"source":{"file":"std/effects/edgeGlow.ts","line":38},"kind":"function","signature":"heartbeatPulse(animTime: number): number","params":[{"name":"args","type":"[animTime: number]","optional":false}]},"effects.edgeGlow.edgeGlowBand":{"id":"effects.edgeGlow.edgeGlowBand","name":"edgeGlowBand","qualifiedName":"edgeGlowBand","category":"effects.edgeGlow","module":"std/effects/edgeGlow.ts","summary":"The glowing band around the shape's edge, with drifting smoke, plus the angle around the shape.","description":"Takes the shape's signed distance (`frame.surf0.member('r')`), the shape-space uv (`frame.sdfUV`), the viewport size, the `scale` prop, `thickness` (0.01 to 1; 1 is about as wide as the default shape), `softness` (0 is a crisp ring, 1 a wide gradient), `smokeSize` and `smokeAmt` (0 to 1), the layer's clock, `pulse` (0 to 1) and the shared `beat`. Returns `.x` the band (0 to 1) and `.y` the angle around the shape in turns (0 to 1), which `orbitSpotsAccum` needs.","examples":["const band = call(edgeGlowBand, 'edgeGlowBand', [frame.surf0!.member('r'), frame.sdfUV!, params.ctx.viewportSize, uniforms.scale, uniforms.thickness, uniforms.softness, uniforms.smokeSize, uniforms.smoke, t, uniforms.pulse, beat])"],"tips":[],"see":["effects.edgeGlow.heartbeatPulse","effects.edgeGlow.orbitSpotsAccum"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.edgeGlow.edgeGlowBand"},"usedBy":["LightEdge"],"documented":true,"source":{"file":"std/effects/edgeGlow.ts","line":67},"kind":"function","signature":"edgeGlowBand(fieldRaw: number, sdfUV: v2f, viewport: v2f, scale: number, thickness: number, softness: number, smokeSize: number, smokeAmt: number, animTime: number, pulse: number, beat: number): v2f","params":[{"name":"args","type":"[fieldRaw: number, sdfUV: v2f, viewport: v2f, scale: number, thickness: number, softness: number, smokeSize: number, smokeAmt: number, animTime: number, pulse: number, beat: number]","optional":false}]},"effects.edgeGlow.orbitSpotsAccum":{"id":"effects.edgeGlow.orbitSpotsAccum","name":"orbitSpotsAccum","qualifiedName":"orbitSpotsAccum","category":"effects.edgeGlow","module":"std/effects/edgeGlow.ts","summary":"The spots of one color orbiting the band, as coverage at this pixel.","description":"Call it once per color. Takes the angle and band from `edgeGlowBand`, the layer's clock, the shared `beat`, `spots` (1 to 5 per color), `spotSize` (0 to 1), `pulse` (0 to 1), `intensity` (0 to 1), `softness`, `seed` and the color's index. Returns a pair for `edgeGlowAccumColorFor`: `.x` the layered coverage and `.y` the additive sum used for bloom.","examples":["const sectors = call(orbitSpotsAccum, 'orbitSpotsAccum', [band.member('y'), band.member('x'), t, beat, uniforms.spots, uniforms.spotSize, uniforms.pulse, uniforms.intensity, uniforms.softness, uniforms.seed, floatE(colorIndex)])"],"tips":[],"see":["effects.edgeGlow.edgeGlowBand","effects.edgeGlow.edgeGlowAccumColorFor"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.edgeGlow.orbitSpotsAccum"},"usedBy":["LightEdge"],"documented":true,"source":{"file":"std/effects/edgeGlow.ts","line":123},"kind":"function","signature":"orbitSpotsAccum(angle01: number, border: number, animTime: number, beat: number, spotsF: number, spotSize: number, pulse: number, intensity: number, softness: number, seed: number, colorIdxF: number): v2f","params":[{"name":"args","type":"[angle01: number, border: number, animTime: number, beat: number, spotsF: number, spotSize: number, pulse: number, intensity: number, softness: number, seed: number, colorIdxF: number]","optional":false}]},"effects.edgeGlow.edgeGlowAccumZero":{"id":"effects.edgeGlow.edgeGlowAccumZero","name":"edgeGlowAccumZero","qualifiedName":"edgeGlowAccumZero","category":"effects.edgeGlow","module":"std/effects/edgeGlow.ts","summary":"The empty state to start folding colors into.","description":"","examples":["let state = call(edgeGlowAccumZero, 'edgeGlowAccumZero', [])"],"tips":[],"see":["effects.edgeGlow.edgeGlowAccumColorFor","effects.edgeGlow.edgeGlowCompose"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.edgeGlow.edgeGlowAccumZero"},"usedBy":["LightEdge"],"documented":true,"source":{"file":"std/effects/edgeGlow.ts","line":178},"kind":"function","signature":"edgeGlowAccumZero(): { rgb: v3f; w: number; ba: number; ac: v3f; aa: number; }","params":[{"name":"args","type":"[]","optional":false}]},"effects.edgeGlow.edgeGlowAccumColorFor":{"id":"effects.edgeGlow.edgeGlowAccumColorFor","name":"edgeGlowAccumColorFor","qualifiedName":"edgeGlowAccumColorFor","category":"effects.edgeGlow","module":"std/effects/edgeGlow.ts","summary":"The fold step for one color space: adds one color's spots to the running state.","description":"`mode` is the compile-time value of a `colorSpace` prop (see `transformColorSpace`); where colors overlap they mix in that space. The returned GPU function takes the state, a color and the pair from `orbitSpotsAccum`, and returns the new state. Call it once per color, then close with `edgeGlowCompose`.","examples":["const accumColor = edgeGlowAccumColorFor(propValues.colorSpace as number)\nstate = call(accumColor, 'edgeGlowAccumColor', [state, uniforms.colorA, sectors])"],"tips":["OKLCh keeps overlapping lights vivid; linear RGB muddies them."],"see":["effects.edgeGlow.edgeGlowAccumZero","effects.edgeGlow.orbitSpotsAccum","effects.edgeGlow.edgeGlowCompose"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.edgeGlow.edgeGlowAccumColorFor"},"usedBy":["LightEdge"],"documented":true,"source":{"file":"std/effects/edgeGlow.ts","line":218},"kind":"function","signature":"edgeGlowAccumColorFor(mode: number): TgpuFn<(st: WgslStruct<{ rgb: Vec3f; w: F32; ba: F32; ac: Vec3f; aa: F32; }>, color: Vec4f, sectors: Vec2f) => WgslStruct<{ rgb: Vec3f; w: F32; ba: F32; ac: Vec3f; aa: F32; }>>","params":[{"name":"mode","type":"number","optional":false}]},"effects.edgeGlow.colorAtIndex":{"id":"effects.edgeGlow.colorAtIndex","name":"colorAtIndex","qualifiedName":"colorAtIndex","category":"effects.edgeGlow","module":"std/effects/edgeGlow.ts","summary":"One color from a `stops` prop, by index.","description":"Reads the packed stops array (`uniforms.colorsArray` when the definition uses `colorStopsPropConfig`). Stop positions are ignored; the stops are a list of orbit colors.","examples":["const color = call(colorAtIndex, 'colorAtIndex', [uniforms.colorsArray, floatE(i)])"],"tips":[],"see":["effects.edgeGlow.edgeGlowAccumColorFor"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.edgeGlow.colorAtIndex"},"usedBy":["LightEdge"],"documented":true,"source":{"file":"std/effects/edgeGlow.ts","line":240},"kind":"function","signature":"colorAtIndex(colors: v4f[], idx: number): v4f","params":[{"name":"args","type":"[colors: v4f[], idx: number]","optional":false}]},"effects.edgeGlow.edgeGlowCompose":{"id":"effects.edgeGlow.edgeGlowCompose","name":"edgeGlowCompose","qualifiedName":"edgeGlowCompose","category":"effects.edgeGlow","module":"std/effects/edgeGlow.ts","summary":"The final color from the folded state, over a transparent background.","description":"`bloom` (0 to 1) sets how additively overlapping lights pile up; high values overdrive to white. Returns straight alpha, ready to return from the surface function.","examples":["return call(edgeGlowCompose, 'edgeGlowCompose', [state, uniforms.bloom])"],"tips":[],"see":["effects.edgeGlow.edgeGlowAccumColorFor"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.edgeGlow.edgeGlowCompose"},"usedBy":["LightEdge"],"documented":true,"source":{"file":"std/effects/edgeGlow.ts","line":261},"kind":"function","signature":"edgeGlowCompose(st: { rgb: v3f; w: number; ba: number; ac: v3f; aa: number; }, bloom: number): v4f","params":[{"name":"args","type":"[st: { rgb: v3f; w: number; ba: number; ac: v3f; aa: number; }, bloom: number]","optional":false}]},"effects.overlay.detectionOverlay":{"id":"effects.overlay.detectionOverlay","name":"detectionOverlay","qualifiedName":"detectionOverlay","category":"effects.overlay","module":"std/effects/overlay.ts","summary":"Draw computer-vision style detection boxes over whatever the child contains.","description":"A whole fragment for a `gpu:` definition. The canvas is cut into cells (`layout`, `cellSize`, `maxDepth`); each cell that holds content of the chosen kind (`detectionMode` above `threshold`) gets a box drawn tight around that content, with an optional label showing its size or coverage. The child shows through underneath. Every slot is a prop ref; the field list on `DetectionOverlaySlots` gives units and options.","examples":["gpu: {fragment: detectionOverlay({detectionMode: p('detectionMode'), threshold: p('threshold'), layout: p('layout'), cellSize: p('cellSize'), maxDepth: p('maxDepth'), boxStyle: p('boxStyle'), lineWidth: p('lineWidth'), cornerRadius: p('cornerRadius'), strokeColor: p('strokeColor'), fillColor: p('fillColor'), labelColor: p('labelColor'), labelBackgroundColor: p('labelBackgroundColor'), labelMode: p('labelMode'), labelPosition: p('labelPosition'), labelInset: p('labelInset'), labelRadius: p('labelRadius'), fontFamily: p('fontFamily'), fontWeight: p('fontWeight'), fontSize: p('fontSize'), letterSpacing: p('letterSpacing')})}"],"tips":["Cost grows with `maxDepth`; 2 is plenty for most layouts. Set `labelMode` to `'none'` to skip the text pass entirely."],"see":["effects.overlay.motionTrackerHud"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.overlay.detectionOverlay"},"usedBy":["ObjectTracker"],"documented":true,"source":{"file":"std/effects/overlay.ts","line":91},"kind":"function","signature":"detectionOverlay(slots: DetectionOverlaySlots): (params: GpuFragmentParams) => ExprT","params":[{"name":"slots","type":"DetectionOverlaySlots","optional":false}]},"effects.overlay.motionTrackerHud":{"id":"effects.overlay.motionTrackerHud","name":"motionTrackerHud","qualifiedName":"motionTrackerHud","category":"effects.overlay","module":"std/effects/overlay.ts","summary":"Draw motion-tracker gizmos that chase content across the child, leaving keyframe trails.","description":"A whole fragment for a `gpu:` definition. It only draws; the trackers themselves come from the tracker pursuit simulation, which the same definition must run as its `compute:` pass (`trackerSim.createTrackerPursuitSim` from the kit, as the KeyFrames shader does). Without that pass the child passes through untouched. Every slot is a prop ref; the field list on `MotionTrackerHudSlots` gives units.","examples":["gpu: {fragment: motionTrackerHud({trackers: p('trackers'), trail: p('trail'), markerSize: p('markerSize'), lineWidth: p('lineWidth'), markerColor: p('markerColor'), keyframeColor: p('keyframeColor'), pathColor: p('pathColor')})}"],"tips":["Copy the KeyFrames shader as a whole; the compute pass and this fragment share the `trackers` prop and the detection props."],"see":["effects.overlay.detectionOverlay"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.overlay.motionTrackerHud"},"usedBy":["KeyFrames"],"documented":true,"source":{"file":"std/effects/overlay.ts","line":223},"kind":"function","signature":"motionTrackerHud(slots: MotionTrackerHudSlots): (params: GpuFragmentParams) => ExprT","params":[{"name":"slots","type":"MotionTrackerHudSlots","optional":false}]},"effects.pointerFields.SPLAT_MIN_GRID":{"id":"effects.pointerFields.SPLAT_MIN_GRID","name":"SPLAT_MIN_GRID","qualifiedName":"SPLAT_MIN_GRID","category":"effects.pointerFields","module":"std/effects/pointerFields.ts","summary":"The smallest grid `pointerSplatField` runs on: 8 cells per side.","description":"","examples":[],"tips":[],"see":[],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.pointerFields.SPLAT_MIN_GRID"},"usedBy":[],"documented":true,"source":{"file":"std/effects/pointerFields.ts","line":29},"kind":"constant","signature":"SPLAT_MIN_GRID: 8","params":[]},"effects.pointerFields.SPLAT_MAX_GRID":{"id":"effects.pointerFields.SPLAT_MAX_GRID","name":"SPLAT_MAX_GRID","qualifiedName":"SPLAT_MAX_GRID","category":"effects.pointerFields","module":"std/effects/pointerFields.ts","summary":"The largest grid `pointerSplatField` runs on: 128 cells per side.","description":"","examples":[],"tips":[],"see":[],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.pointerFields.SPLAT_MAX_GRID"},"usedBy":[],"documented":true,"source":{"file":"std/effects/pointerFields.ts","line":31},"kind":"constant","signature":"SPLAT_MAX_GRID: 128","params":[]},"effects.pointerFields.clampSplatGridSize":{"id":"effects.pointerFields.clampSplatGridSize","name":"clampSplatGridSize","qualifiedName":"clampSplatGridSize","category":"effects.pointerFields","module":"std/effects/pointerFields.ts","summary":"The grid size `pointerSplatField` really uses for a prop value: floored, then held to 8–128.","description":"Use it in the `gridSize` prop's recompile rule, so the simulation rebuilds only when the cell count changes and not on every drag of the slider.","examples":["gridSize: {default: 20, recompile: recompileWhen((prev, next) => clampSplatGridSize(prev as number) !== clampSplatGridSize(next as number))}"],"tips":[],"see":["effects.pointerFields.pointerSplatField"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.pointerFields.clampSplatGridSize"},"usedBy":["GridDistortion"],"documented":true,"source":{"file":"std/effects/pointerFields.ts","line":48},"kind":"function","signature":"clampSplatGridSize(v: number): number","params":[{"name":"v","type":"number","optional":false}]},"effects.pointerFields.pointerSplatField":{"id":"effects.pointerFields.pointerSplatField","name":"pointerSplatField","qualifiedName":"pointerSplatField","category":"effects.pointerFields","module":"std/effects/pointerFields.ts","summary":"A field the cursor smears: cells near the pointer move with it, then fade back to rest.","description":"Spread it with `...sim.grids.gridSim(...)`. `gridSize` is the cells per side (8 to 128, see `clampSplatGridSize`). `decay` (about 0 to 10) is how fast the smear fades; `intensity` (about 0 to 5) how hard the cursor pushes; `radius` the brush size (1 is about 5% of the canvas). The field is published under `output` for a warp map such as `warps.gridCellDisplace`.","examples":["...sim.grids.gridSim(pointerSplatField({gridSize: p('gridSize'), decay: p('decay'), intensity: p('intensity'), radius: p('radius'), output: 'displacement'}))"],"tips":["Add `uvRemapIdentityWhen: ({computeOutputs}) => !computeOutputs?.displacement` so the warp is a no-op before the field exists."],"see":["effects.pointerFields.springLatticeField","effects.pointerFields.clampSplatGridSize"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.pointerFields.pointerSplatField"},"usedBy":["GridDistortion"],"documented":true,"source":{"file":"std/effects/pointerFields.ts","line":124},"kind":"function","signature":"pointerSplatField(slots: {\n    gridSize: PropRef\n    decay: PropRef\n    intensity: PropRef\n    radius: PropRef\n    output: string\n}): (params: GpuFragmentParams, root: GridRoot) => GridSimConfig","params":[{"name":"slots","type":"{\n    gridSize: PropRef\n    decay: PropRef\n    intensity: PropRef\n    radius: PropRef\n    output: string\n}","optional":false}]},"effects.pointerFields.springLatticeField":{"id":"effects.pointerFields.springLatticeField","name":"springLatticeField","qualifiedName":"springLatticeField","category":"effects.pointerFields","module":"std/effects/pointerFields.ts","summary":"A cloth of springs the cursor pushes: it stretches, rings and settles like fabric.","description":"Spread it with `...sim.grids.gridSim(...)`. `stiffness` (about 1 to 30) is how rigid the cloth is; `damping` (about 0 to 10) how quickly it stops moving; `radius` the push area (1 is about 8% of the canvas). The grid is fixed at 64 cells per side. The field is published under `output` for a warp map such as `warps.liquidDisplace`.","examples":["...sim.grids.gridSim(springLatticeField({stiffness: p('stiffness'), damping: p('damping'), radius: p('radius'), output: 'displacement'}))"],"tips":["Add `uvRemapIdentityWhen: ({computeOutputs}) => !computeOutputs?.displacement` so the warp is a no-op before the field exists."],"see":["effects.pointerFields.pointerSplatField"],"import":{"statement":"import {effects} from 'shaders/std'","usage":"effects.pointerFields.springLatticeField"},"usedBy":["Liquify"],"documented":true,"source":{"file":"std/effects/pointerFields.ts","line":311},"kind":"function","signature":"springLatticeField(slots: {\n    stiffness: PropRef\n    damping: PropRef\n    radius: PropRef\n    output: string\n}): (params: GpuFragmentParams, root: GridRoot) => GridSimConfig","params":[{"name":"slots","type":"{\n    stiffness: PropRef\n    damping: PropRef\n    radius: PropRef\n    output: string\n}","optional":false}]},"simulate.op.wave":{"id":"simulate.op.wave","name":"wave","qualifiedName":"op.wave","category":"simulate","module":"std/sim.ts","summary":"A wave step: the grid ripples outward from any disturbance and fades by `damping` (a prop, 0–20).","description":"","examples":["op.wave({damping: p('decay')})"],"tips":["Declare `history: 2` on the grid. A wave needs the two previous frames."],"see":["simulate.op.splat"],"import":{"statement":"import {op} from 'shaders/std'","usage":"op.wave"},"usedBy":["CursorRipples"],"documented":true,"source":{"file":"std/sim.ts","line":97},"kind":"function","signature":"op.wave(config: {damping: PropRef}): WaveOp","params":[{"name":"config","type":"{damping: PropRef}","optional":false}]},"simulate.op.splat":{"id":"simulate.op.splat","name":"splat","qualifiedName":"op.splat","category":"simulate","module":"std/sim.ts","summary":"A brush step: a soft round push into the grid at the pointer, scaled by how fast it moves.","description":"`radius` is a prop on a 0.1–1 scale.","examples":["op.splat({at: pointer({teleportGuard: 'on'}), amount: pointerSpeed({max: 2}), radius: p('radius')})"],"tips":["Use `pointer({teleportGuard: 'on'})`. The pointer entering the canvas is then not a stroke."],"see":["simulate.op.wave","invoke.ctx.pointer","signal.pointerSpeed"],"import":{"statement":"import {op} from 'shaders/std'","usage":"op.splat"},"usedBy":["CursorRipples"],"documented":true,"source":{"file":"std/sim.ts","line":111},"kind":"function","signature":"op.splat(config: {at: PointerSignal; amount: PointerSpeedSignal; radius: PropRef}): SplatOp","params":[{"name":"config","type":"{at: PointerSignal; amount: PointerSpeedSignal; radius: PropRef}","optional":false}]},"simulate.op.gradient":{"id":"simulate.op.gradient","name":"gradient","qualifiedName":"op.gradient","category":"simulate","module":"std/sim.ts","summary":"A derived output: the grid's slope at every cell as a 2D vector, ready for `displaceBy`.","description":"","examples":["derive: {displacement: op.gradient()}"],"tips":[],"see":["filter.displaceBy"],"import":{"statement":"import {op} from 'shaders/std'","usage":"op.gradient"},"usedBy":["CursorRipples"],"documented":true,"source":{"file":"std/sim.ts","line":123},"kind":"function","signature":"op.gradient(): GradientOp","params":[]},"simulate.op":{"id":"simulate.op","name":"op","qualifiedName":"op","category":"simulate","module":"std/sim.ts","summary":"The steps and outputs a grid simulation is built from.","description":"`op.wave` and `op.splat` go in `step` and run in order once per frame. `op.gradient` goes in `derive`, named, and becomes an output an effect can read with `sim.output('name')`.","examples":["step: [op.wave({damping: p('decay')}), op.splat({at: pointer({teleportGuard: 'on'}), amount: pointerSpeed({max: 2}), radius: p('radius')})],\nderive: {displacement: op.gradient()},"],"tips":["The current engine implements exactly `[op.wave, op.splat]` with `op.gradient` derived. Other combinations throw at definition time."],"see":["simulate.simulate","simulate.GridSim"],"import":{"statement":"import {op} from 'shaders/std'","usage":"op"},"usedBy":["CursorRipples"],"documented":true,"source":{"file":"std/sim.ts","line":85},"kind":"object","signature":"op: { wave, splat, gradient }","members":["simulate.op.wave","simulate.op.splat","simulate.op.gradient"],"params":[]},"simulate.GridSim":{"id":"simulate.GridSim","name":"GridSim","qualifiedName":"GridSim","category":"simulate","module":"std/sim.ts","summary":"A declared grid simulation.","description":"Read its outputs with `output('name')` and pass them to an effect.","examples":["effect: displaceBy(waves.output('displacement'), {strength: p('intensity'), chromatic: p('chromaticSplit'), edges: p('edges')})"],"tips":["Asking for an output that was not declared in `derive` throws with the list of names that were."],"see":["simulate.simulate","SimOutputRef"],"import":{"statement":"import {GridSim} from 'shaders/std'","usage":"GridSim"},"usedBy":[],"documented":true,"source":{"file":"std/sim.ts","line":170},"kind":"class","signature":"class GridSim","params":[]},"simulate.simulate.grid":{"id":"simulate.simulate.grid","name":"grid","qualifiedName":"simulate.grid","category":"simulate","module":"std/sim.ts","summary":"A square grid of numbers advanced once per frame by `step`, with named outputs in `derive`.","description":"","examples":["const waves = simulate.grid({resolution: 128, history: 2, step: [op.wave({damping: p('decay')}), op.splat({at: pointer({teleportGuard: 'on'}), amount: pointerSpeed({max: 2}), radius: p('radius')})], derive: {displacement: op.gradient()}})"],"tips":[],"see":["simulate.op","simulate.GridSim"],"import":{"statement":"import {simulate} from 'shaders/std'","usage":"simulate.grid"},"usedBy":["CursorRipples"],"documented":true,"source":{"file":"std/sim.ts","line":206},"kind":"function","signature":"simulate.grid(config: GridSimConfig): GridSim","params":[{"name":"config","type":"GridSimConfig","optional":false}]},"simulate.simulate":{"id":"simulate.simulate","name":"simulate","qualifiedName":"simulate","category":"simulate","module":"std/sim.ts","summary":"Declare a simulation the engine runs for you.","description":"`simulate.grid` gives a square grid of numbers advanced once per frame by the steps you list.\n\nDeclare it once at module scope, then read its outputs inside the definition. The engine allocates the state, keeps the history, tracks the pointer and sleeps the simulation once it has settled.","examples":["const waves = simulate.grid({resolution: 128, history: 2, step: [op.wave({damping: p('decay')}), op.splat({at: pointer({teleportGuard: 'on'}), amount: pointerSpeed({max: 2}), radius: p('radius')})], derive: {displacement: op.gradient()}, rest: {settlesWhen: 'derived-from-damping'}})"],"tips":["Reach for `sim.grids`, `sim.fluids`, `sim.feedback` or `sim.agents` only when the effect needs its own GPU step. If a wave field and a displacement say it, stay here."],"see":["simulate.op","simulate.GridSim","filter.displaceBy"],"import":{"statement":"import {simulate} from 'shaders/std'","usage":"simulate"},"usedBy":["CursorRipples"],"documented":true,"source":{"file":"std/sim.ts","line":196},"kind":"object","signature":"simulate: { grid }","members":["simulate.simulate.grid"],"params":[]},"sim.grids.gridSim":{"id":"sim.grids.gridSim","name":"gridSim","qualifiedName":"gridSim","category":"sim.grids","module":"std/sim/grids.ts","summary":"A field of cells advanced every frame by an ordered list of stages.","description":"Spread it into a definition next to a `gpu.fragment` that samples one of its outputs.\n\nThe build callback runs once per instance of the layer, with the GPU device, and returns the outputs, the frame-delta clamp and the stages. Return null to run nothing (a required child is missing). Without a GPU the fragment should fall back to the child or to transparent.","examples":["...gridSim((params, root) => ({outputs: {state}, clampDt: 0.05, stages: [op.values('write', (f) => paramsU.write({dt: f.dt, radius: f.num('radius', 0.4)})), op.pass(update), op.publish(() => output)]}))"],"tips":["Everything created in the build callback (buffers, passes, trackers) belongs to that layer instance. Create nothing at module scope that holds per-frame state."],"see":["sim.grids.op","sim.grids.hostGridProgram","simulate.simulate","sim.fluids.fluidSim","sim.feedback.feedbackSim"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.grids.gridSim"},"usedBy":["GridDistortion","Liquify","PixelSort","ReactionDiffusion"],"documented":true,"source":{"file":"std/sim/grids.ts","line":126},"kind":"function","signature":"gridSim(build: (params: GpuFragmentParams, root: GridRoot) => GridSimConfig | null): {compute: GpuComputeNode}","params":[{"name":"build","type":"(params: GpuFragmentParams, root: GridRoot) => GridSimConfig | null","optional":false}]},"sim.grids.op.host":{"id":"sim.grids.op.host","name":"host","qualifiedName":"op.host","category":"sim.grids","module":"std/sim/grids.ts","summary":"A named step that runs before any gate: pointer smoothing, activity flags, anything a later `settle` needs to know.","description":"","examples":["op.host('cursor', (f) => { active = (f.frameParams.pointer?.x ?? 0.5) !== lastX })"],"tips":[],"see":["sim.grids.op.values","sim.grids.op.settle"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.grids.op.host"},"usedBy":[],"documented":true,"source":{"file":"std/sim/grids.ts","line":197},"kind":"function","signature":"op.host(name: string, run: (f: GridFrame) => void): GridStage","params":[{"name":"name","type":"string","optional":false},{"name":"run","type":"(f: GridFrame) => void","optional":false}]},"sim.grids.op.values":{"id":"sim.grids.op.values","name":"values","qualifiedName":"op.values","category":"sim.grids","module":"std/sim/grids.ts","summary":"A named step that writes this frame's values for the passes after it.","description":"","examples":["op.values('write', (f) => paramsU.write({dt: f.dt, radius: f.num('radius', 0.4)}))"],"tips":["Read props here with `f.num('prop', fallback)` so a prop that is briefly not a number cannot reach the GPU as `undefined`."],"see":["sim.grids.op.host","sim.grids.op.pass"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.grids.op.values"},"usedBy":["PixelSort","ReactionDiffusion"],"documented":true,"source":{"file":"std/sim/grids.ts","line":213},"kind":"function","signature":"op.values(name: string, run: (f: GridFrame) => void): GridStage","params":[{"name":"name","type":"string","optional":false},{"name":"run","type":"(f: GridFrame) => void","optional":false}]},"sim.grids.op.settle":{"id":"sim.grids.op.settle","name":"settle","qualifiedName":"op.settle","category":"sim.grids","module":"std/sim/grids.ts","summary":"Sleep the simulation once nothing has driven it for `settleMs` of simulated time.","description":"Frames are skipped and the last published texture stays on screen, so a still field costs nothing.\n\n`activeWhen` says whether something drove the field this frame. `settleMs` is how long the field takes to fade after the last activity, in milliseconds.","examples":["op.settle({activeWhen: () => moving, settleMs: (f) => 4000 / Math.max(f.num('decay', 1), 0.1)})"],"tips":["Simulated time, not wall-clock: a hidden tab does not count, so the field is still fading when the user comes back."],"see":["sim.grids.op.host"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.grids.op.settle"},"usedBy":[],"documented":true,"source":{"file":"std/sim/grids.ts","line":236},"kind":"function","signature":"op.settle(opts: {activeWhen: (f: GridFrame) => boolean; settleMs: (f: GridFrame) => number}): GridStage","params":[{"name":"opts","type":"{activeWhen: (f: GridFrame) => boolean; settleMs: (f: GridFrame) => number}","optional":false}]},"sim.grids.op.readyWhen":{"id":"sim.grids.op.readyWhen","name":"readyWhen","qualifiedName":"op.readyWhen","category":"sim.grids","module":"std/sim/grids.ts","summary":"Run nothing until `fn` returns true, typically once the child's picture has arrived through `bindInputs`.","description":"","examples":["op.readyWhen(() => lumaReady)"],"tips":[],"see":["sim.grids.op.cache"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.grids.op.readyWhen"},"usedBy":["PixelSort","ReactionDiffusion"],"documented":true,"source":{"file":"std/sim/grids.ts","line":261},"kind":"function","signature":"op.readyWhen(fn: () => boolean): GridStage","params":[{"name":"fn","type":"() => boolean","optional":false}]},"sim.grids.op.pass":{"id":"sim.grids.op.pass","name":"pass","qualifiedName":"op.pass","category":"sim.grids","module":"std/sim/grids.ts","summary":"One GPU pass, the same every frame.","description":"","examples":["op.pass(update)"],"tips":[],"see":["sim.grids.op.cache","sim.grids.op.iterate","sim.grids.op.publish"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.grids.op.pass"},"usedBy":[],"documented":true,"source":{"file":"std/sim/grids.ts","line":274},"kind":"function","signature":"op.pass(step: ComputeStep): GridStage","params":[{"name":"step","type":"ComputeStep","optional":false}]},"sim.grids.op.cache":{"id":"sim.grids.op.cache","name":"cache","qualifiedName":"op.cache","category":"sim.grids","module":"std/sim/grids.ts","summary":"A GPU pass chosen per frame, for a pass whose input is rebound after the frame starts.","description":"","examples":["op.cache(() => lumaPipeline)"],"tips":[],"see":["sim.grids.op.readyWhen","sim.grids.op.pass"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.grids.op.cache"},"usedBy":["PixelSort"],"documented":true,"source":{"file":"std/sim/grids.ts","line":288},"kind":"function","signature":"op.cache(step: (f: GridFrame) => ComputeStep): GridStage","params":[{"name":"step","type":"(f: GridFrame) => ComputeStep","optional":false}]},"sim.grids.op.publish":{"id":"sim.grids.op.publish","name":"publish","qualifiedName":"op.publish","category":"sim.grids","module":"std/sim/grids.ts","summary":"The last stage: write the state into the texture the layer samples.","description":"Chosen per frame so it can follow whichever buffer holds the current result.","examples":["op.publish(() => outputPass.with(sort.side() === 'A' ? outputBgA : outputBgB))"],"tips":[],"see":["sim.grids.op.pass","sim.grids.op.sortPass"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.grids.op.publish"},"usedBy":["PixelSort","ReactionDiffusion"],"documented":true,"source":{"file":"std/sim/grids.ts","line":302},"kind":"function","signature":"op.publish(step: (f: GridFrame) => ComputeStep): GridStage","params":[{"name":"step","type":"(f: GridFrame) => ComputeStep","optional":false}]},"sim.grids.op.seedOnce":{"id":"sim.grids.op.seedOnce","name":"seedOnce","qualifiedName":"op.seedOnce","category":"sim.grids","module":"std/sim/grids.ts","summary":"A one-shot reset run before the rest of the frame whenever `staleWhen` says the state is stale: on the first frame, or when a seed prop changes.","description":"","examples":["op.seedOnce({pass: seed, staleWhen: () => needsSeed, onSeed: () => { needsSeed = false }})"],"tips":[],"see":["sim.grids.op.iterate"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.grids.op.seedOnce"},"usedBy":["ReactionDiffusion"],"documented":true,"source":{"file":"std/sim/grids.ts","line":316},"kind":"function","signature":"op.seedOnce(opts: {pass: ComputeStep; staleWhen: (f: GridFrame) => boolean; onSeed?: () => void}): GridStage","params":[{"name":"opts","type":"{pass: ComputeStep; staleWhen: (f: GridFrame) => boolean; onSeed?: () => void}","optional":false}]},"sim.grids.op.iterate":{"id":"sim.grids.op.iterate","name":"iterate","qualifiedName":"op.iterate","category":"sim.grids","module":"std/sim/grids.ts","summary":"Run a step `count` times this frame.","description":"Your `step` adds the pass for iteration `i` and picks which buffer it reads and writes.","examples":["op.iterate({count: (f) => Math.round(f.num('speed', 8)), step: (i, nodes) => nodes.push(react.with(i % 2 ? groupBA : groupAB))})"],"tips":["A reaction-diffusion field reads smoother when you raise the iteration count than when you raise the step size."],"see":["sim.grids.op.pass","sim.grids.op.sortPass"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.grids.op.iterate"},"usedBy":["ReactionDiffusion"],"documented":true,"source":{"file":"std/sim/grids.ts","line":338},"kind":"function","signature":"op.iterate(opts: {count: (f: GridFrame) => number; step: (i: number, nodes: ComputeStep[], f: GridFrame) => void}): GridStage","params":[{"name":"opts","type":"{count: (f: GridFrame) => number; step: (i: number, nodes: ComputeStep[], f: GridFrame) => void}","optional":false}]},"sim.grids.op.sortPass":{"id":"sim.grids.op.sortPass","name":"sortPass","qualifiedName":"op.sortPass","category":"sim.grids","module":"std/sim/grids.ts","summary":"A compare-and-swap sort that converges a little more every frame: `passes` swaps per frame, alternating odd and even pairs and the two buffers.","description":"`side()` tells `publish` which buffer holds the current order.","examples":["const sort = op.sortPass({passes: () => 1 + Math.round(strength * 4), pass: (parity, side) => (parity ? swap1 : swap0).with(side === 'A' ? groupAB : groupBA)})"],"tips":["The alternation continues across frames instead of restarting, which is what makes the sort settle bit by bit."],"see":["sim.grids.op.publish","sim.grids.op.iterate"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.grids.op.sortPass"},"usedBy":["PixelSort"],"documented":true,"source":{"file":"std/sim/grids.ts","line":362},"kind":"function","signature":"op.sortPass(opts: {passes: (f: GridFrame) => number; pass: (parity: 0 | 1, side: 'A' | 'B') => ComputeStep}): SortPassStage","params":[{"name":"opts","type":"{passes: (f: GridFrame) => number; pass: (parity: 0 | 1, side: 'A' | 'B') => ComputeStep}","optional":false}]},"sim.grids.op":{"id":"sim.grids.op","name":"op","qualifiedName":"op","category":"sim.grids","module":"std/sim/grids.ts","summary":"The stages a grid frame is built from.","description":"List them in `stages` in the order they should run.\n\nBookkeeping: `host` (before any gate), `values` (write this frame's values), `readyWhen` (wait for an input), `settle` (sleep once the field is still). Work: `pass` (one GPU pass), `cache` (a pass picked per frame), `seedOnce` (a one-shot reset), `iterate` (a pass N times), `sortPass` (a compare-swap chain), `publish` (write the result the layer samples).","examples":["stages: [op.readyWhen(() => ready), op.values('write', (f) => paramsU.write({dt: f.dt})), op.iterate({count: () => 8, step: (i, nodes) => nodes.push(react.with(i % 2 ? groupBA : groupAB))}), op.publish(() => output)]"],"tips":["A `values` stage sits before the passes that read it. Stages run in list order."],"see":["sim.grids.gridSim"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.grids.op"},"usedBy":["PixelSort","ReactionDiffusion"],"documented":true,"source":{"file":"std/sim/grids.ts","line":185},"kind":"object","signature":"op: { host, values, settle, readyWhen, pass, cache, publish, seedOnce, iterate, sortPass }","members":["sim.grids.op.host","sim.grids.op.values","sim.grids.op.settle","sim.grids.op.readyWhen","sim.grids.op.pass","sim.grids.op.cache","sim.grids.op.publish","sim.grids.op.seedOnce","sim.grids.op.iterate","sim.grids.op.sortPass"],"params":[]},"sim.grids.trackedViewport":{"id":"sim.grids.trackedViewport","name":"trackedViewport","qualifiedName":"trackedViewport","category":"sim.grids","module":"std/sim/grids.ts","summary":"The canvas size in pixels, kept current across resizes, in the two forms a stage wants.","description":"`tracked()` is the size at the last resize, for a pass that works in the canvas's own pixels. `safe(fp)` prefers the size this frame reports and never returns less than 1, for an aspect ratio.","examples":["const viewport = trackedViewport(params)\nop.values('write', (f) => { const s = viewport.safe(f.frameParams); paramsU.write({aspect: s.width / s.height}) })"],"tips":[],"see":["sim.grids.gridSim"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.grids.trackedViewport"},"usedBy":["PixelSort"],"documented":true,"source":{"file":"std/sim/grids.ts","line":395},"kind":"function","signature":"trackedViewport(params: GpuFragmentParams): {\n    tracked(): {width: number; height: number}\n    safe(fp: GridFrameParams): {width: number; height: number}\n}","params":[{"name":"params","type":"GpuFragmentParams","optional":false}]},"sim.grids.hostStep":{"id":"sim.grids.hostStep","name":"hostStep","qualifiedName":"hostStep","category":"sim.grids","module":"std/sim/grids.ts","summary":"One named step of a JavaScript-stepped grid.","description":"Return `'skip'` to end the frame here, which is how an idle gate is written.","examples":["hostStep('dtGuard', (f) => (f.dt <= 0 ? 'skip' : undefined))"],"tips":[],"see":["sim.grids.hostGridProgram"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.grids.hostStep"},"usedBy":["ChromaFlow","PixelThrow"],"documented":true,"source":{"file":"std/sim/grids.ts","line":446},"kind":"function","signature":"hostStep(name: string, run: (f: HostFrame) => void | 'skip'): HostStep","params":[{"name":"name","type":"string","optional":false},{"name":"run","type":"(f: HostFrame) => void | 'skip'","optional":false}]},"sim.grids.hostGridProgram":{"id":"sim.grids.hostGridProgram","name":"hostGridProgram","qualifiedName":"hostGridProgram","category":"sim.grids","module":"std/sim/grids.ts","summary":"A frame program for a small grid stepped in plain JavaScript: the steps run in order every frame until one returns `'skip'`.","description":"Hand the result to `onBeforeRender`.\n\nUse it when the field is small (a 32×32 flow grid) and the arithmetic reads better as loops. Publish the cells with `hostFieldTexture` in the last step.","examples":["onBeforeRender(hostGridProgram({clampDt: 0.016, steps: [hostStep('advect', (f) => advect(f.dt)), hostStep('publish', () => { pack(field.texData); field.upload() })]}))"],"tips":[],"see":["sim.grids.hostStep","sim.grids.hostFieldTexture","sim.grids.gridSim"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.grids.hostGridProgram"},"usedBy":["ChromaFlow","PixelThrow"],"documented":true,"source":{"file":"std/sim/grids.ts","line":462},"kind":"function","signature":"hostGridProgram(opts: {clampDt: number; steps: HostStep[]}): (fp: unknown) => void","params":[{"name":"opts","type":"{clampDt: number; steps: HostStep[]}","optional":false}]},"sim.grids.hostFieldTexture":{"id":"sim.grids.hostFieldTexture","name":"hostFieldTexture","qualifiedName":"hostFieldTexture","category":"sim.grids","module":"std/sim/grids.ts","summary":"The texture a JavaScript-stepped grid is read through.","description":"Pack the cells into `texData` (four half-floats per cell, row by row), call `upload()`, and sample `texture` in the fragment.\n\n`size` is the grid's side in cells. The texture filters smoothly, so a coarse grid reads as a continuous field.","examples":["const field = hostFieldTexture(params, {size: 32, label: 'flow'})"],"tips":["Values go in as half-floats: convert each number with `toHalfFloat` before writing it into `texData`."],"see":["sim.grids.hostGridProgram"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.grids.hostFieldTexture"},"usedBy":["ChromaFlow","PixelThrow"],"documented":true,"source":{"file":"std/sim/grids.ts","line":491},"kind":"function","signature":"hostFieldTexture(params: GpuFragmentParams, opts: {size: number; label: string}): {texData: Uint16Array; texture: ReturnType<GpuFragmentParams['registerMediaTexture']>; upload(): void}","params":[{"name":"params","type":"GpuFragmentParams","optional":false},{"name":"opts","type":"{size: number; label: string}","optional":false}]},"sim.fluids.ambientForce":{"id":"sim.fluids.ambientForce","name":"ambientForce","qualifiedName":"ambientForce","category":"sim.fluids","module":"std/sim/fluids.ts","summary":"A force applied to the whole field before every solve: a turbulence field, a steady wind.","description":"","examples":["solve: [ambientForce(turbulenceKernel)]"],"tips":[],"see":["sim.fluids.restoreToward","sim.fluids.fluidSim"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.fluids.ambientForce"},"usedBy":["Fog"],"documented":true,"source":{"file":"std/sim/fluids.ts","line":139},"kind":"function","signature":"ambientForce(kernel: FluidKernel): FluidSolveStage","params":[{"name":"kernel","type":"FluidKernel","optional":false}]},"sim.fluids.restoreToward":{"id":"sim.fluids.restoreToward","name":"restoreToward","qualifiedName":"restoreToward","category":"sim.fluids","module":"std/sim/fluids.ts","summary":"A correction applied after every solve: re-seed a color that the solve slowly blurs away.","description":"","examples":["solve: [restoreToward(colorRestoreKernel)]"],"tips":[],"see":["sim.fluids.ambientForce","sim.fluids.fluidSim"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.fluids.restoreToward"},"usedBy":["Fog"],"documented":true,"source":{"file":"std/sim/fluids.ts","line":151},"kind":"function","signature":"restoreToward(kernel: FluidKernel): FluidSolveStage","params":[{"name":"kernel","type":"FluidKernel","optional":false}]},"sim.fluids.fluidSim":{"id":"sim.fluids.fluidSim","name":"fluidSim","qualifiedName":"fluidSim","category":"sim.fluids","module":"std/sim/fluids.ts","summary":"A fluid the engine solves every frame.","description":"Spread it into a definition next to a `gpu.fragment` that samples the output texture under `output.key`.\n\nThe build callback runs once per instance of the layer, with the GPU device, and returns the grid size, the solver, the output pass, the parts and the `values` function. Everything created inside it belongs to that instance. Without a GPU nothing runs and the fragment should fall back to transparent.","examples":["...fluidSim(() => ({resolution: 256, layout: fluidLayout, outputLayout, paramsSchema: FluidParams, solver: {kernels: solverKernels, jacobiIters: 10}, output: {kernel: outputKernel, key: 'smokeTexture'}, pointer: {minDrag: 0.0005}, inject: [splat({kernel: splatKernel})], values: (f) => ({dt: f.dt, emitX: 0.5 * 256, emitY: 256, emitRad: f.num('emitRadius', 0.06) * 256, dyeFade: f.num('dissipation', 1), velFade: 0.2, curlStrength: f.num('detail', 30), colorDecay: 1})}))"],"tips":["Positions and radii in `values` are in grid cells, not uv: multiply a uv value by `resolution`."],"see":["sim.fluids.splat","sim.fluids.cursorRibbon","sim.fluids.seededFieldInit","sim.fluids.ambientForce","sim.fluids.restoreToward","simulate.simulate"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.fluids.fluidSim"},"usedBy":["Fog","InkFlow","Smoke","SmokeFill","SmokeFlow"],"documented":true,"source":{"file":"std/sim/fluids.ts","line":240},"kind":"function","signature":"fluidSim<TParams>(build: (params: GpuFragmentParams, root: FluidRoot) => FluidSimConfig<TParams>): {compute: GpuComputeNode}","params":[{"name":"build","type":"(params: GpuFragmentParams, root: FluidRoot) => FluidSimConfig<TParams>","optional":false}]},"sim.fluids.splat":{"id":"sim.fluids.splat","name":"splat","qualifiedName":"splat","category":"sim.fluids","module":"std/sim/fluids.ts","summary":"An emitter at a fixed source: one pass per frame that adds dye and momentum where your pass function says (a cone from a point, a line along an edge).","description":"Give it the pass function, or a `setup` that builds the pass on resources of its own.","examples":["inject: [splat({kernel: splatKernel})]"],"tips":["The source position and radius come from `values`, in grid cells."],"see":["sim.fluids.cursorRibbon","sim.fluids.fluidSim"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.fluids.splat"},"usedBy":["Smoke","SmokeFill"],"documented":true,"source":{"file":"std/sim/fluids.ts","line":366},"kind":"function","signature":"splat<TParams = unknown>(opts: {kernel: FluidKernel} | {setup: (ctx: FluidSetupCtx) => KitComputePipeline}): FluidEmitter<TParams>","params":[{"name":"opts","type":"{kernel: FluidKernel} | {setup: (ctx: FluidSetupCtx) => KitComputePipeline}","optional":false}]},"sim.fluids.cursorRibbon":{"id":"sim.fluids.cursorRibbon","name":"cursorRibbon","qualifiedName":"cursorRibbon","category":"sim.fluids","module":"std/sim/fluids.ts","summary":"An emitter that paints along the pointer's stroke: a ribbon of stamps between last frame's position and this one, so a fast flick leaves a continuous line, not dots.","description":"It tracks the pointer itself, marks the frame active when `activeWhen` says so, and sleeps the simulation `fadeSeconds` after the last activity. `ribbon` describes this frame's stamps. Read the pointer back with `.ptr()` and `.active()` inside `values`.","examples":["const strokes = cursorRibbon<Params>({activeWhen: (ptr) => ptr.smoothSpeed > 0.005, fadeSeconds: (f) => decayFadeSeconds(255, f.num('dissipation', 0.4)), pass: {kernel: splatKernel}, ribbon: (f, ptr, values) => ({stepSize: Math.max(0.004, f.num('emitRadius', 0.07) * 0.5), maxSteps: 64, write: (emitX, emitY) => f.writeParams({...values, emitX, emitY})})})"],"tips":["Raise the tracker's `teleportGuard` when fast flicks should paint: the default treats a quarter-canvas jump as the cursor re-entering and emits nothing."],"see":["sim.fluids.splat","sim.fluids.fluidSim"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.fluids.cursorRibbon"},"usedBy":["InkFlow","SmokeFlow"],"documented":true,"source":{"file":"std/sim/fluids.ts","line":413},"kind":"function","signature":"cursorRibbon<TParams = unknown, TPrepared = undefined>(opts: {\n    /** Pointer tracking: smoothing, the teleport distance, the minimum drag. */\n    tracker?: PointerVelocityTrackerOptions\n    /** Whether the stroke emits this frame. A live frame keeps the simulation awake. */\n    activeWhen: (ptr: PointerFrame, f: FluidFrame) => boolean\n    /** Seconds after the last activity before the simulation may sleep. */\n    fadeSeconds: (f: FluidFrame) => number\n    /** Extra bookkeeping per frame before the idle gate. */\n    // InkFlow's color-cycle time drift.\n    onFrame?: (f: FluidFrame, ptr: PointerFrame) => void\n    /** The stamp pass: a pass function, or a `setup` that builds one on resources of its own. */\n    pass: {kernel: FluidKernel} | {setup: (ctx: FluidSetupCtx) => KitComputePipeline}\n    /** This frame's stamps. */\n    ribbon: (f: FluidFrame, ptr: PointerFrame, values: TParams) => CursorRibbonSpec<TPrepared>\n}): FluidEmitter<TParams> & {ptr(): PointerFrame | null; active(): boolean}","params":[{"name":"opts","type":"{\n    /** Pointer tracking: smoothing, the teleport distance, the minimum drag. */\n    tracker?: PointerVelocityTrackerOptions\n    /** Whether the stroke emits this frame. A live frame keeps the simulation awake. */\n    activeWhen: (ptr: PointerFrame, f: FluidFrame) => boolean\n    /** Seconds after the last activity before the simulation may sleep. */\n    fadeSeconds: (f: FluidFrame) => number\n    /** Extra bookkeeping per frame before the idle gate. */\n    // InkFlow's color-cycle time drift.\n    onFrame?: (f: FluidFrame, ptr: PointerFrame) => void\n    /** The stamp pass: a pass function, or a `setup` that builds one on resources of its own. */\n    pass: {kernel: FluidKernel} | {setup: (ctx: FluidSetupCtx) => KitComputePipeline}\n    /** This frame's stamps. */\n    ribbon: (f: FluidFrame, ptr: PointerFrame, values: TParams) => CursorRibbonSpec<TPrepared>\n}","optional":false}]},"sim.fluids.seededFieldInit":{"id":"sim.fluids.seededFieldInit","name":"seededFieldInit","qualifiedName":"seededFieldInit","category":"sim.fluids","module":"std/sim/fluids.ts","summary":"A field that starts full: seed it from a pass function, then run the solve silently `warm.steps` times so the first visible frame already flows.","description":"Re-seeds whenever `seed` returns a new value. Each warm step advances a private clock by `warm.dt` seconds and writes `warm.values(t)`. When it finishes, the simulation's clock continues from where the warm-up ended.","examples":["init: seededFieldInit({kernel: initKernel, seed: (f) => f.num('seed', 0), warm: {steps: 50, jacobiIters: 10, dt: 0.033, startTime: () => Math.random() * 100, values: (t, f) => ({...baseValues(f), time: t})}})"],"tips":["A random `startTime` makes two instances of the same layer drift differently."],"see":["sim.fluids.fluidSim","sim.fluids.ambientForce"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.fluids.seededFieldInit"},"usedBy":["Fog"],"documented":true,"source":{"file":"std/sim/fluids.ts","line":485},"kind":"function","signature":"seededFieldInit<TParams>(opts: {\n    kernel: FluidKernel\n    /** The value whose change forces a re-seed. */\n    seed: (f: FluidFrame) => number\n    warm: {\n        /** How many silent solves to run. */\n        steps: number\n        jacobiIters: number\n        /** Simulated seconds per warm step. */\n        dt: number\n        /** Where the warm clock starts. Randomize it so two instances differ. */\n        startTime: () => number\n        /** The full values for a warm step at simulated time `t`. `t = 0` is the seed write. */\n        values: (t: number, f: FluidFrame) => TParams\n    }\n}): FluidInitPart<TParams>","params":[{"name":"opts","type":"{\n    kernel: FluidKernel\n    /** The value whose change forces a re-seed. */\n    seed: (f: FluidFrame) => number\n    warm: {\n        /** How many silent solves to run. */\n        steps: number\n        jacobiIters: number\n        /** Simulated seconds per warm step. */\n        dt: number\n        /** Where the warm clock starts. Randomize it so two instances differ. */\n        startTime: () => number\n        /** The full values for a warm step at simulated time `t`. `t = 0` is the seed write. */\n        values: (t: number, f: FluidFrame) => TParams\n    }\n}","optional":false}]},"sim.feedback.feedbackSim":{"id":"sim.feedback.feedbackSim","name":"feedbackSim","qualifiedName":"feedbackSim","category":"sim.feedback","module":"std/sim/feedback.ts","summary":"A simulation that advances a picture of its own last frame by one step of your rule, every frame.","description":"Spread it into a definition next to a `gpu.fragment` that samples `display`.\n\nThe build callback runs once per instance of the layer, so anything you create inside it (resources, a kernel variant picked from a compile-time prop) belongs to that instance. The simulation needs a child. Without one, or without a GPU, nothing runs and the fragment should fall back to the child.","examples":["...feedbackSim((params, root) => ({size: 768, format: 'rgba16float', speedProp: 'speed', paramsSchema: decoder.Params, bindGroups: ({childTexture, display, paramsBuffer}, read, write) => root.createBindGroup(decoder.layout, {src: childTexture, prev: read.state, next: write.state, display, params: paramsBuffer} as never), step: decoder.kernel, values: ({dt, localTime}) => ({time: localTime, dt, intensity: params.getCpuValue('intensity') as number})}))"],"tips":["Read time from `localTime`, not the layer's clock. It stops when `speedProp` is 0 and every frame's step matches it."],"see":["sim.fluids.fluidSim","sim.grids.gridSim","sim.agents.agentSim","simulate.simulate"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.feedback.feedbackSim"},"usedBy":["DataMosh","TimeTrail"],"documented":true,"source":{"file":"std/sim/feedback.ts","line":118},"kind":"function","signature":"feedbackSim<TParams, TGroups = unknown>(build: (params: GpuFragmentParams, root: FeedbackRoot) => FeedbackSimConfig<TParams, TGroups>): {compute: GpuComputeNode}","params":[{"name":"build","type":"(params: GpuFragmentParams, root: FeedbackRoot) => FeedbackSimConfig<TParams, TGroups>","optional":false}]},"sim.agents.renderAgents.orientedWorld":{"id":"sim.agents.renderAgents.orientedWorld","name":"orientedWorld","qualifiedName":"renderAgents.orientedWorld","category":"sim.agents","module":"std/sim/agents.ts","summary":"Agents with a heading, drawn as a shape pointing the way they move (or the way they are turned), colored from a rest color to an excited color.","description":"`heading: 'velocity'` reads the direction from the agent's velocity, `'angle'` from a stored angle plus a `home` part. `agitation: 'buffer'` reads excitement from a per-agent value, `'speed'` from the agent's speed. `comet: true` stretches a glow along the heading. `res` is the canvas side in texels.","examples":["renderAgents.orientedWorld(simLayout, {shape, res: 1024, splatRCap: 16, heading: 'velocity', agitation: 'buffer', comet: true, ramp: {colorSpace: 2, trails: 'on'}, names: {splat: 'boidsSplat', resolve: 'boidsResolve'}})"],"tips":[],"see":["sim.agents.renderAgents.pointWorld","sim.agents.renderAgents.volume"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agents.renderAgents.orientedWorld"},"usedBy":["Boids","MagneticFilings","ParticleFlow"],"documented":true,"source":{"file":"std/sim/agents.ts","line":85},"kind":"function","signature":"renderAgents.orientedWorld(layout: WorldSplatLayout & RampResolveLayout, cfg: Omit<OrientedWorldSplatConfig, 'name'> & {\n            ramp: Omit<RampResolveConfig, 'name' | 'res' | 'invGain'> & {invGain?: number}\n            names: {splat: string; resolve: string}\n            /** Give the splat the second resource group as well (a shape family's own resources). */\n            extraOnSplat?: boolean\n        }): RenderAgentsPipelines","params":[{"name":"layout","type":"WorldSplatLayout & RampResolveLayout","optional":false},{"name":"cfg","type":"Omit<OrientedWorldSplatConfig, 'name'> & {\n            ramp: Omit<RampResolveConfig, 'name' | 'res' | 'invGain'> & {invGain?: number}\n            names: {splat: string; resolve: string}\n            /** Give the splat the second resource group as well (a shape family's own resources). */\n            extraOnSplat?: boolean\n        }","optional":false}]},"sim.agents.renderAgents.pointWorld":{"id":"sim.agents.renderAgents.pointWorld","name":"pointWorld","qualifiedName":"renderAgents.pointWorld","category":"sim.agents","module":"std/sim/agents.ts","summary":"Round motes with no heading, drawn in one tint with a live softness.","description":"Where each mote is placed, how bright it is and how big it is come from `pointSlot` parts.","examples":["renderAgents.pointWorld(simLayout, {shape, res: 1024, place: pointSlot.orbitalPlace(simLayout, {radius: 0.035, rate: 1.4}), brightness: pointSlot.twinkleBrightness(simLayout, {freq: 2}), bodyRadius: pointSlot.presenceRadius(simLayout), names: {splat: 'motesSplat', resolve: 'motesResolve'}})"],"tips":[],"see":["sim.agents.renderAgents.orientedWorld","sim.agentRender.pointSlot"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agents.renderAgents.pointWorld"},"usedBy":["FloatingParticles"],"documented":true,"source":{"file":"std/sim/agents.ts","line":125},"kind":"function","signature":"renderAgents.pointWorld(layout: PointWorldSplatLayout & Parameters<typeof resolve.tint>[0], cfg: Omit<PointWorldSplatConfig, 'name'> & {names: {splat: string; resolve: string}}): RenderAgentsPipelines","params":[{"name":"layout","type":"PointWorldSplatLayout & Parameters<typeof resolve.tint>[0]","optional":false},{"name":"cfg","type":"Omit<PointWorldSplatConfig, 'name'> & {names: {splat: string; resolve: string}}","optional":false}]},"sim.agents.renderAgents.volume":{"id":"sim.agents.renderAgents.volume","name":"volume","qualifiedName":"renderAgents.volume","category":"sim.agents","module":"std/sim/agents.ts","summary":"A 3D swarm seen in perspective: nearer particles draw larger and brighter, and the color runs from rest to excited with speed.","description":"","examples":["renderAgents.volume(simLayout, {shape, outRes: 1024, maxSplatSize: 12, extQ: 6, ramp: {colorSpace: 0, exposure: true, trails: 'none'}, names: {splat: 'swarmSplat', glow: 'swarmGlow', profile: 'swarmProfile', resolve: 'swarmResolve'}, extraOnSplat: true})"],"tips":[],"see":["sim.agents.renderAgents.orientedWorld","sim.agents.renderAgents.relief"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agents.renderAgents.volume"},"usedBy":["Particles"],"documented":true,"source":{"file":"std/sim/agents.ts","line":151},"kind":"function","signature":"renderAgents.volume(layout: Parameters<typeof splat.volume>[0] & RampResolveLayout, cfg: Omit<VolumeSplatConfig, 'names'> & {\n            ramp: Omit<RampResolveConfig, 'name' | 'res' | 'invGain'>\n            names: VolumeSplatConfig['names'] & {resolve: string}\n            extraOnSplat?: boolean\n        }): RenderAgentsPipelines","params":[{"name":"layout","type":"Parameters<typeof splat.volume>[0] & RampResolveLayout","optional":false},{"name":"cfg","type":"Omit<VolumeSplatConfig, 'names'> & {\n            ramp: Omit<RampResolveConfig, 'name' | 'res' | 'invGain'>\n            names: VolumeSplatConfig['names'] & {resolve: string}\n            extraOnSplat?: boolean\n        }","optional":false}]},"sim.agents.renderAgents.relief":{"id":"sim.agents.renderAgents.relief","name":"relief","qualifiedName":"renderAgents.relief","category":"sim.agents","module":"std/sim/agents.ts","summary":"Particles standing on an image, seen through a camera: each carries the child's color at its home, and nearer particles win the color where they overlap.","description":"`out` is the output size in texels.","examples":["renderAgents.relief(fieldLayout, {shape, splatRCap: 24, aaW: 0.75, minAlpha: 0.02, fp: 1024, wobbleFreq: 1.3, perspK: 0.9, out: [outW, outH], weightedColor: {alphaK: 1.6}, names: {splat: 'reliefSplat', resolve: 'reliefResolve'}})"],"tips":[],"see":["sim.agents.renderAgents.volume"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agents.renderAgents.relief"},"usedBy":["ParticleField"],"documented":true,"source":{"file":"std/sim/agents.ts","line":186},"kind":"function","signature":"renderAgents.relief(layout: Parameters<typeof splat.relief>[0], cfg: Omit<ReliefSplatConfig, 'name'> & {\n            out: [number, number]\n            weightedColor: {alphaK: number}\n            names: {splat: string; resolve: string}\n        }): RenderAgentsPipelines","params":[{"name":"layout","type":"Parameters<typeof splat.relief>[0]","optional":false},{"name":"cfg","type":"Omit<ReliefSplatConfig, 'name'> & {\n            out: [number, number]\n            weightedColor: {alphaK: number}\n            names: {splat: string; resolve: string}\n        }","optional":false}]},"sim.agents.renderAgents":{"id":"sim.agents.renderAgents","name":"renderAgents","qualifiedName":"renderAgents","category":"sim.agents","module":"std/sim/agents.ts","summary":"How the agents are drawn, as one declared part: a splat (each agent stamps its shape into a shared canvas) paired with the matching resolve (the canvas becomes the output picture).","description":"Four variants, one per kind of agent: `orientedWorld` for agents with a heading (arrows, streaks, comets) colored from rest to excited, `pointWorld` for round motes in one tint, `volume` for a 3D swarm seen in perspective, `relief` for particles that stand on an image. Put the returned `splat` and `resolve` into `bake`'s pipelines and end the program with them.","examples":["const render = renderAgents.orientedWorld(simLayout, {shape: 'arrow', res: 1024, splatRCap: 16, heading: 'velocity', agitation: 'buffer', comet: true, ramp: {colorSpace: 2, trails: 'on'}, names: {splat: 'boidsSplat', resolve: 'boidsResolve'}})"],"tips":["The resolve also clears the canvas as it reads it, so no clear step is needed."],"see":["sim.agents.agentSim","simulate.op.splat","sim.agentRender.resolve","sim.agentRender.pointSlot"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agents.renderAgents"},"usedBy":["Boids","FloatingParticles","MagneticFilings","ParticleField","ParticleFlow","Particles"],"documented":true,"source":{"file":"std/sim/agents.ts","line":68},"kind":"object","signature":"renderAgents: { orientedWorld, pointWorld, volume, relief }","members":["sim.agents.renderAgents.orientedWorld","sim.agents.renderAgents.pointWorld","sim.agents.renderAgents.volume","sim.agents.renderAgents.relief"],"params":[]},"sim.agents.agentSim":{"id":"sim.agents.agentSim","name":"agentSim","qualifiedName":"agentSim","category":"sim.agents","module":"std/sim/agents.ts","summary":"A simulation of many agents, spread into a definition: `bake` assembles the steps from your declared parts, `frame` writes the per-frame values, and the fragment samples the picture.","description":"`bake` runs once per composition with the compile-time props (shape, color space) and returns the pipelines and their order. `frame` runs once per frame: read props, write `sys.writeParams(...)`, return `sys.frame({count})`. The spawn step runs before the first frame and again whenever `reseed` changes. Without a GPU the fragment shows the fallback.","examples":["...agentSim<typeof SimParams>({layout: simLayout, params: SimParams, maxAgents: 4096, countCap: {desktop: 4096, mobile: 1200}, output: {key: 'outTex', name: 'boidsTexture', size: [1024, 1024], format: 'rgba16float'}, bake: ({getCpuValue}) => { const render = boidsRender(getCpuValue('agentShape') as string); return {pipelines: {init: {kernel: boidsInitKernel, threads: 'max'}, update: {kernel: boidsUpdateKernel, threads: 'agents'}, splat: render.splat, resolve: render.resolve}, initStep: 'init', program: ['update', 'splat', 'resolve']} }, frame: (sys, {getCpuValue}) => (frameParams) => { const {dt, aspect} = readAgentFrame(frameParams); const count = sys.resolveCount(getCpuValue('count') as number); sys.writeParams({...}); return sys.frame({count}) }, fragment: {output: 'boidsTexture', fallback: 'transparent'}})"],"tips":["Clamp the count prop with `sys.resolveCount` rather than reading it raw, so a desktop preset still loads on a phone."],"see":["sim.agents.renderAgents","sim.agentForces.integrator","sim.agentForces.force","agentFrame","simulate.simulate"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agents.agentSim"},"usedBy":["Boids","FloatingParticles","MagneticFilings","ParticleField","ParticleFlow","Particles"],"documented":true,"source":{"file":"std/sim/agents.ts","line":289},"kind":"function","signature":"agentSim<TParams extends AnyWgslStruct>(cfg: AgentSimConfig<TParams>): AgentSimSpread","params":[{"name":"cfg","type":"AgentSimConfig<TParams>","optional":false}]},"sim.agentForces.FieldSample":{"id":"sim.agentForces.FieldSample","name":"FieldSample","qualifiedName":"FieldSample","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"What a field sample carries: the direction away from the source (`rhat`), the `strength`, and the line angle `phi` in radians.","description":"Build one when writing your own `AgentFieldAt`.","examples":["return FieldSample({rhat, strength: fieldStr, phi: std.atan2(B.y, B.x)})"],"tips":[],"see":["sim.agentForces.field.dipoleOrRadial","sim.agentForces.torque"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.FieldSample"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":597},"kind":"function","signature":"FieldSample(props: { rhat: v2f; strength: number; phi: number; }): { rhat: v2f; strength: number; phi: number; }","params":[{"name":"props","type":"{ rhat: v2f; strength: number; phi: number; }","optional":false}]},"sim.agentForces.channel.luminance":{"id":"sim.agentForces.channel.luminance","name":"luminance","qualifiedName":"channel.luminance","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"The color's brightness, 0 black to 1 white.","description":"","examples":["channel: channel.luminance"],"tips":[],"see":[],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.channel.luminance"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":965},"kind":"function","signature":"channel.luminance: AgentChannel","params":[]},"sim.agentForces.channel.luminanceInverted":{"id":"sim.agentForces.channel.luminanceInverted","name":"luminanceInverted","qualifiedName":"channel.luminanceInverted","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"One minus the brightness, so dark areas rise.","description":"","examples":["channel: channel.luminanceInverted"],"tips":[],"see":[],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.channel.luminanceInverted"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":974},"kind":"function","signature":"channel.luminanceInverted: AgentChannel","params":[]},"sim.agentForces.channel.red":{"id":"sim.agentForces.channel.red","name":"red","qualifiedName":"channel.red","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"The red channel.","description":"","examples":["channel: channel.red"],"tips":[],"see":[],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.channel.red"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":983},"kind":"function","signature":"channel.red: AgentChannel","params":[]},"sim.agentForces.channel.green":{"id":"sim.agentForces.channel.green","name":"green","qualifiedName":"channel.green","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"The green channel.","description":"","examples":["channel: channel.green"],"tips":[],"see":[],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.channel.green"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":992},"kind":"function","signature":"channel.green: AgentChannel","params":[]},"sim.agentForces.channel.blue":{"id":"sim.agentForces.channel.blue","name":"blue","qualifiedName":"channel.blue","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"The blue channel.","description":"","examples":["channel: channel.blue"],"tips":[],"see":[],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.channel.blue"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":1001},"kind":"function","signature":"channel.blue: AgentChannel","params":[]},"sim.agentForces.channel.saturation":{"id":"sim.agentForces.channel.saturation","name":"saturation","qualifiedName":"channel.saturation","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"How colorful the pixel is, 0 grey to 1 pure.","description":"","examples":["channel: channel.saturation"],"tips":[],"see":[],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.channel.saturation"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":1010},"kind":"function","signature":"channel.saturation: AgentChannel","params":[]},"sim.agentForces.channel.alpha":{"id":"sim.agentForces.channel.alpha","name":"alpha","qualifiedName":"channel.alpha","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"The pixel's alpha.","description":"","examples":["channel: channel.alpha"],"tips":[],"see":[],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.channel.alpha"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":1019},"kind":"function","signature":"channel.alpha: AgentChannel","params":[]},"sim.agentForces.channel.byMode":{"id":"sim.agentForces.channel.byMode","name":"byMode","qualifiedName":"channel.byMode","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"The same seven by a select prop's number: 0 luminance, 1 inverted, 2 red, 3 green, 4 blue, 5 saturation, 6 alpha.","description":"","examples":["channel: channel.byMode[channelMode] ?? channel.luminance"],"tips":[],"see":[],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.channel.byMode"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":1029},"kind":"constant","signature":"channel.byMode: Record<number, AgentChannel>","params":[]},"sim.agentForces.channel":{"id":"sim.agentForces.channel","name":"channel","qualifiedName":"channel","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"Which part of the child's color drives a particle: `luminance`, `luminanceInverted`, `red`, `green`, `blue`, `saturation` or `alpha`, each a number in 0–1.","description":"`byMode` maps a select prop's number (0 luminance … 6 alpha) to the same functions.","examples":["integrator.relief(fieldLayout, {channel: channel.byMode[channelMode] ?? channel.luminance, cursor: force.cursorInView(fieldLayout, {perspK: 0.9}), stiffness: 18, maxSpeed: 4, minAlpha: 0.02, name: 'reliefUpdate'})"],"tips":[],"see":["sim.agentForces.integrator"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.channel"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":956},"kind":"object","signature":"channel: { luminance, luminanceInverted, red, green, blue, saturation, alpha, byMode }","members":["sim.agentForces.channel.luminance","sim.agentForces.channel.luminanceInverted","sim.agentForces.channel.red","sim.agentForces.channel.green","sim.agentForces.channel.blue","sim.agentForces.channel.saturation","sim.agentForces.channel.alpha","sim.agentForces.channel.byMode"],"params":[]},"sim.agentForces.force.pressure":{"id":"sim.agentForces.force.pressure","name":"pressure","qualifiedName":"force.pressure","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"Spread a 3D swarm evenly by pushing agents away from crowded cells of a density grid.","description":"Returns the force plus the two grid steps to put in the program before the update.","examples":["const pressure = force.pressure(simLayout, {gridDim: 32, domain: 1, names: {clear: 'swarmClearDensity', splat: 'swarmDensity'}})"],"tips":["Register `pressure.clearKernel` as a `fixed` step over `pressure.cells` and `pressure.splatKernel` over `agents`, both before the update."],"see":["sim.agentForces.force.containment","sim.agentForces.integrator.forces3d"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.force.pressure"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":110},"kind":"function","signature":"force.pressure(layout: VolumeSwarmLayout, cfg: {\n    gridDim: number\n    /** The grid covers shape-local [−domain, domain]³. */\n    domain: number\n    names: {clear: string; splat: string}\n}): PressureForce","params":[{"name":"layout","type":"VolumeSwarmLayout","optional":false},{"name":"cfg","type":"{\n    gridDim: number\n    /** The grid covers shape-local [−domain, domain]³. */\n    domain: number\n    names: {clear: string; splat: string}\n}","optional":false}]},"sim.agentForces.force.containment":{"id":"sim.agentForces.force.containment","name":"containment","qualifiedName":"force.containment","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"Keep a 3D swarm inside a shape: a spring at the surface, a pull back from far away, and a drag when the shape rotates.","description":"","examples":["forces: [pressure.force, force.containment(simLayout, {field: hexField, gradEps: 0.02, wall: {featherFrom: -0.1, featherTo: 0.01, base: 1.1, springK: 22}, recall: {from: 0.85, to: 1.1, k: 9}, homing: {from: 0.12, to: 0.45, k: 5}, entrainment: {featherHalf: 0.05}})]"],"tips":[],"see":["sim.shapeFields.shapeField","sim.agentForces.force.pressure"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.force.containment"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":239},"kind":"function","signature":"force.containment(layout: VolumeSwarmLayout, cfg: {\n    field: ShapeField3\n    /** How far apart the two samples of the field's slope are taken. 0.02 is typical. */\n    // Central-difference epsilon for the field gradient.\n    gradEps: number\n    /** The spring at the surface: fades in from `featherFrom` to `featherTo` (signed distance), `base` push plus `springK` per unit outside. */\n    // Strong outside, diffuse inside (a sharp feather packs a coherent shell).\n    wall: {featherFrom: number; featherTo: number; base: number; springK: number}\n    /** The pull home once an agent is `from`..`to` from the origin, with strength `k`. */\n    // A hard clamp piles strays into lines.\n    recall: {from: number; to: number; k: number}\n    /** The pull toward the middle where the signed distance is `from`..`to`, with strength `k`. */\n    // SDF-VALUE-driven pull toward the origin — direction-safe where the gradient degenerates.\n    homing: {from: number; to: number; k: number}\n    /** How wide a band around the surface the rotation drag fades over. */\n    // Drag toward the rotating container's feature velocity ω×r, feathered to inside.\n    entrainment: {featherHalf: number}\n}): AgentForce3","params":[{"name":"layout","type":"VolumeSwarmLayout","optional":false},{"name":"cfg","type":"{\n    field: ShapeField3\n    /** How far apart the two samples of the field's slope are taken. 0.02 is typical. */\n    // Central-difference epsilon for the field gradient.\n    gradEps: number\n    /** The spring at the surface: fades in from `featherFrom` to `featherTo` (signed distance), `base` push plus `springK` per unit outside. */\n    // Strong outside, diffuse inside (a sharp feather packs a coherent shell).\n    wall: {featherFrom: number; featherTo: number; base: number; springK: number}\n    /** The pull home once an agent is `from`..`to` from the origin, with strength `k`. */\n    // A hard clamp piles strays into lines.\n    recall: {from: number; to: number; k: number}\n    /** The pull toward the middle where the signed distance is `from`..`to`, with strength `k`. */\n    // SDF-VALUE-driven pull toward the origin — direction-safe where the gradient degenerates.\n    homing: {from: number; to: number; k: number}\n    /** How wide a band around the surface the rotation drag fades over. */\n    // Drag toward the rotating container's feature velocity ω×r, feathered to inside.\n    entrainment: {featherHalf: number}\n}","optional":false}]},"sim.agentForces.force.cursorXY":{"id":"sim.agentForces.force.cursorXY","name":"cursorXY","qualifiedName":"force.cursorXY","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"The cursor as a magnet on a 3D swarm, pushing away (or pulling in when the force is negative).","description":"","examples":["forces: [pressure.force, force.cursorXY(simLayout), force.gravity(simLayout)]"],"tips":[],"see":["sim.agentForces.force.gravity","sim.agentForces.force.turbulence"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.force.cursorXY"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":300},"kind":"function","signature":"force.cursorXY(layout: VolumeSwarmLayout): AgentForce3","params":[{"name":"layout","type":"VolumeSwarmLayout","optional":false}]},"sim.agentForces.force.gravity":{"id":"sim.agentForces.force.gravity","name":"gravity","qualifiedName":"force.gravity","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"A constant pull on a 3D swarm in the direction of `params.gravX, gravY`.","description":"","examples":["forces: [pressure.force, force.cursorXY(simLayout), force.gravity(simLayout)]"],"tips":[],"see":["sim.agentForces.force.cursorXY"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.force.gravity"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":311},"kind":"function","signature":"force.gravity(layout: VolumeSwarmLayout): AgentForce3","params":[{"name":"layout","type":"VolumeSwarmLayout","optional":false}]},"sim.agentForces.force.turbulence":{"id":"sim.agentForces.force.turbulence","name":"turbulence","qualifiedName":"force.turbulence","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"A per-agent jitter that keeps a 3D swarm alive, scaled by `params.agitation`.","description":"","examples":["forces: [pressure.force, force.turbulence(simLayout, {posScale: 193, timeX: 31.7, timeY: 27.3, seedScale: 997, gain: 5})]"],"tips":["Keep the scale constants large. Small ones make the noise smooth and the whole swarm drifts in step."],"see":["sim.agentForces.force.cursorXY"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.force.turbulence"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":329},"kind":"function","signature":"force.turbulence(layout: VolumeSwarmLayout, cfg: {posScale: number; timeX: number; timeY: number; seedScale: number; gain: number}): AgentForce3","params":[{"name":"layout","type":"VolumeSwarmLayout","optional":false},{"name":"cfg","type":"{posScale: number; timeX: number; timeY: number; seedScale: number; gain: number}","optional":false}]},"sim.agentForces.force.flocking":{"id":"sim.agentForces.force.flocking","name":"flocking","qualifiedName":"force.flocking","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"Separation, alignment and cohesion for a flock, with live weights and radii.","description":"","examples":["forces: [force.flocking(simLayout, {maxAgents: 4096}), force.cursorSteer(simLayout), force.wallTurn(simLayout)]"],"tips":[],"see":["sim.agentForces.force.cursorSteer","sim.agentForces.force.wallTurn","sim.agentForces.integrator.steering2d"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.force.flocking"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":435},"kind":"function","signature":"force.flocking(layout: SteeringLayout, cfg: {maxAgents: number}): AgentSteer2","params":[{"name":"layout","type":"SteeringLayout","optional":false},{"name":"cfg","type":"{maxAgents: number}","optional":false}]},"sim.agentForces.force.cursorSteer":{"id":"sim.agentForces.force.cursorSteer","name":"cursorSteer","qualifiedName":"force.cursorSteer","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"The cursor on a flock: attract, scatter like a predator, or nothing.","description":"","examples":["forces: [force.flocking(simLayout, {maxAgents: 4096}), force.cursorSteer(simLayout)]"],"tips":[],"see":["sim.agentForces.force.flocking"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.force.cursorSteer"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":477},"kind":"function","signature":"force.cursorSteer(layout: SteeringLayout): AgentSteer2","params":[{"name":"layout","type":"SteeringLayout","optional":false}]},"sim.agentForces.force.wallTurn":{"id":"sim.agentForces.force.wallTurn","name":"wallTurn","qualifiedName":"force.wallTurn","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"A soft turn back toward the canvas near an edge, so a flock arcs instead of bouncing.","description":"","examples":["forces: [force.flocking(simLayout, {maxAgents: 4096}), force.wallTurn(simLayout)]"],"tips":[],"see":["sim.agentForces.force.flocking"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.force.wallTurn"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":498},"kind":"function","signature":"force.wallTurn(layout: SteeringLayout): AgentSteer2","params":[{"name":"layout","type":"SteeringLayout","optional":false}]},"sim.agentForces.force.cursorGust":{"id":"sim.agentForces.force.cursorGust","name":"cursorGust","qualifiedName":"force.cursorGust","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"A puff from the cursor on drifting motes, zero while the force slider sits at 0.","description":"","examples":["integrator.drift2d(simLayout, {drift: drift.variedHeading(simLayout), gust: force.cursorGust(simLayout), name: 'motesUpdate'})"],"tips":[],"see":["sim.agentForces.drift.variedHeading","sim.agentForces.integrator.drift2d"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.force.cursorGust"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":836},"kind":"function","signature":"force.cursorGust(layout: DriftLayout): (pos: d.v2f) => d.v2f","params":[{"name":"layout","type":"DriftLayout","optional":false}]},"sim.agentForces.force.cursorInView":{"id":"sim.agentForces.force.cursorInView","name":"cursorInView","qualifiedName":"force.cursorInView","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"The cursor on an image relief, applied where each particle appears on screen.","description":"","examples":["integrator.relief(fieldLayout, {channel: channel.luminance, cursor: force.cursorInView(fieldLayout, {perspK: 0.9}), stiffness: 18, maxSpeed: 4, minAlpha: 0.02, name: 'reliefUpdate'})"],"tips":[],"see":["sim.agentForces.integrator.relief","sim.agentForces.channel"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.force.cursorInView"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":1043},"kind":"function","signature":"force.cursorInView(layout: ReliefLayout, cfg: {perspK: number}): (homeW: d.v2f, off: d.v2f, z: number) => d.v3f","params":[{"name":"layout","type":"ReliefLayout","optional":false},{"name":"cfg","type":"{perspK: number}","optional":false}]},"sim.agentForces.force":{"id":"sim.agentForces.force","name":"force","qualifiedName":"force","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"The pushes an agent can feel.","description":"Each is built over your layout and returns a part the matching integrator folds in list order.\n\nFor a 3D swarm (`integrator.forces3d`): `pressure` (spread out evenly), `containment` (stay inside the shape), `cursorXY`, `gravity`, `turbulence`. For a flock (`integrator.steering2d`): `flocking`, `cursorSteer`, `wallTurn`. For drifting motes (`integrator.drift2d`): `cursorGust`. For an image relief (`integrator.relief`): `cursorInView`.","examples":["integrator.steering2d(simLayout, {forces: [force.flocking(simLayout, {maxAgents: 4096}), force.cursorSteer(simLayout), force.wallTurn(simLayout)], agitation: {rest: 1.5, gain: 0.45, cool: 1.3}, cruiseFloor: 0.35, wallRestitution: 0.6, name: 'boidsUpdate'})"],"tips":["Order matters only for readability. The forces are summed, so list them from the most important down."],"see":["sim.agentForces.integrator","sim.agentForces.torque","sim.shapeFields.shapeField","agentFrame"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.force"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":1245},"kind":"object","signature":"force: { pressure, containment, cursorXY, gravity, turbulence, flocking, cursorSteer, wallTurn, cursorGust, cursorInView }","members":["sim.agentForces.force.pressure","sim.agentForces.force.containment","sim.agentForces.force.cursorXY","sim.agentForces.force.gravity","sim.agentForces.force.turbulence","sim.agentForces.force.flocking","sim.agentForces.force.cursorSteer","sim.agentForces.force.wallTurn","sim.agentForces.force.cursorGust","sim.agentForces.force.cursorInView"],"params":[]},"sim.agentForces.torque.alignToField":{"id":"sim.agentForces.torque.alignToField","name":"alignToField","qualifiedName":"torque.alignToField","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"Swing onto the field's line like a compass needle.","description":"","examples":["torques: [torque.alignToField(simLayout), torque.rest(simLayout)]"],"tips":[],"see":["sim.agentForces.torque.rest","sim.agentForces.field.dipoleOrRadial"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.torque.alignToField"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":678},"kind":"function","signature":"torque.alignToField(layout: OrientationLayout): AgentTorque","params":[{"name":"layout","type":"OrientationLayout","optional":false}]},"sim.agentForces.torque.rest":{"id":"sim.agentForces.torque.rest","name":"rest","qualifiedName":"torque.rest","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"A weak swing back to each agent's rest angle, felt only where the field is faint.","description":"","examples":["torques: [torque.alignToField(simLayout), torque.rest(simLayout)]"],"tips":[],"see":["sim.agentForces.torque.alignToField"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.torque.rest"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":1382},"kind":"function","signature":"torque.rest: (layout: OrientationLayout) => AgentTorque","params":[]},"sim.agentForces.torque":{"id":"sim.agentForces.torque","name":"torque","qualifiedName":"torque","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"The turns an agent that spins in place can feel, for `integrator.orientation2d`.","description":"","examples":["torques: [torque.alignToField(simLayout), torque.rest(simLayout)]"],"tips":[],"see":["sim.agentForces.field","sim.agentForces.integrator"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.torque"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":1362},"kind":"object","signature":"torque: { alignToField, rest }","members":["sim.agentForces.torque.alignToField","sim.agentForces.torque.rest"],"params":[]},"sim.agentForces.field.dipoleOrRadial":{"id":"sim.agentForces.field.dipoleOrRadial","name":"dipoleOrRadial","qualifiedName":"field.dipoleOrRadial","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"The field of a magnet at the cursor: a bar magnet along its travel, or a single pole.","description":"","examples":["integrator.orientation2d(simLayout, {home, fieldAt: field.dipoleOrRadial(simLayout), torques, maxOffset: 0.12, name: 'filingsUpdate'})"],"tips":[],"see":["sim.agentForces.field.jitteredGridHome","sim.agentForces.torque.alignToField"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.field.dipoleOrRadial"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":650},"kind":"function","signature":"field.dipoleOrRadial(layout: OrientationLayout): AgentFieldAt","params":[{"name":"layout","type":"OrientationLayout","optional":false}]},"sim.agentForces.field.jitteredGridHome":{"id":"sim.agentForces.field.jitteredGridHome","name":"jitteredGridHome","qualifiedName":"field.jitteredGridHome","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"Each agent's resting position on a jittered grid that runs past the canvas edges.","description":"","examples":["const home = field.jitteredGridHome(simLayout, {overscan: 0.14})"],"tips":["The same home part goes to `renderAgents.orientedWorld` as `home`, so agents are drawn where they rest."],"see":["sim.agentForces.field.dipoleOrRadial"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.field.jitteredGridHome"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":709},"kind":"function","signature":"field.jitteredGridHome(layout: OrientationLayout, cfg: {overscan: number}): AgentHome2","params":[{"name":"layout","type":"OrientationLayout","optional":false},{"name":"cfg","type":"{overscan: number}","optional":false}]},"sim.agentForces.field":{"id":"sim.agentForces.field","name":"field","qualifiedName":"field","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"What a turning agent samples at its position: the magnetic field at the cursor, and its resting place on a jittered grid.","description":"","examples":["integrator.orientation2d(simLayout, {home: field.jitteredGridHome(simLayout, {overscan: 0.1}), fieldAt: field.dipoleOrRadial(simLayout), torques: [torque.alignToField(simLayout), torque.rest(simLayout)], maxOffset: 0.02, name: 'filingsUpdate'})"],"tips":[],"see":["sim.agentForces.torque","sim.agentForces.integrator","agentFrame"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.field"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":1395},"kind":"object","signature":"field: { dipoleOrRadial, jitteredGridHome }","members":["sim.agentForces.field.dipoleOrRadial","sim.agentForces.field.jitteredGridHome"],"params":[]},"sim.agentForces.drift.variedHeading":{"id":"sim.agentForces.drift.variedHeading","name":"variedHeading","qualifiedName":"drift.variedHeading","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"One shared heading and speed, each varied a little per mote.","description":"","examples":["integrator.drift2d(simLayout, {drift: drift.variedHeading(simLayout), gust: force.cursorGust(simLayout), name: 'motesUpdate'})"],"tips":[],"see":["sim.agentForces.force.cursorGust","sim.agentForces.integrator.drift2d"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.drift.variedHeading"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":822},"kind":"function","signature":"drift.variedHeading(layout: {readonly $: {readonly params: HeadingDriftParamsView}}): AgentDrift","params":[{"name":"layout","type":"{readonly $: {readonly params: HeadingDriftParamsView}}","optional":false}]},"sim.agentForces.drift":{"id":"sim.agentForces.drift","name":"drift","qualifiedName":"drift","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"A built-in velocity for motes that glide, for `integrator.drift2d`.","description":"","examples":["integrator.drift2d(simLayout, {drift: drift.variedHeading(simLayout), gust: force.cursorGust(simLayout), name: 'motesUpdate'})"],"tips":[],"see":["sim.agentForces.integrator"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.drift"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":1428},"kind":"object","signature":"drift: { variedHeading }","members":["sim.agentForces.drift.variedHeading"],"params":[]},"sim.agentForces.integrator.forces3d":{"id":"sim.agentForces.integrator.forces3d","name":"forces3d","qualifiedName":"integrator.forces3d","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"A 3D swarm under summed forces, with drag, a speed limit and a position clamp.","description":"","examples":["integrator.forces3d(simLayout, {forces: [pressure.force, force.containment(simLayout, containmentCfg), force.cursorXY(simLayout), force.gravity(simLayout)], maxSpeed: 3, posClamp: 1.9, name: 'swarmUpdate'})"],"tips":[],"see":["sim.agentForces.force.pressure","sim.agentForces.force.containment"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.integrator.forces3d"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":357},"kind":"function","signature":"integrator.forces3d(layout: VolumeSwarmLayout, cfg: {\n    forces: AgentForce3[]\n    maxSpeed: number\n    posClamp: number\n    name: string\n}): (i: number) => void","params":[{"name":"layout","type":"VolumeSwarmLayout","optional":false},{"name":"cfg","type":"{\n    forces: AgentForce3[]\n    maxSpeed: number\n    posClamp: number\n    name: string\n}","optional":false}]},"sim.agentForces.integrator.steering2d":{"id":"sim.agentForces.integrator.steering2d","name":"steering2d","qualifiedName":"integrator.steering2d","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"A flock under summed steering, kept cruising, arcing off the canvas edges.","description":"","examples":["integrator.steering2d(simLayout, {forces: [force.flocking(simLayout, {maxAgents: 4096}), force.cursorSteer(simLayout), force.wallTurn(simLayout)], agitation: {rest: 1.5, gain: 0.45, cool: 1.3}, cruiseFloor: 0.35, wallRestitution: 0.6, name: 'boidsUpdate'})"],"tips":[],"see":["sim.agentForces.force.flocking"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.integrator.steering2d"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":530},"kind":"function","signature":"integrator.steering2d(layout: SteeringLayout, cfg: {\n    forces: AgentSteer2[]\n    /** Excitement: effort above `rest` (in max-force units) charges by `gain`, and cools at `cool` per second. */\n    agitation: {rest: number; gain: number; cool: number}\n    /** Slowest cruise, as a fraction of each agent's top speed. */\n    cruiseFloor: number\n    /** Speed kept when an agent bounces off an edge (0–1). */\n    wallRestitution: number\n    name: string\n}): (i: number) => void","params":[{"name":"layout","type":"SteeringLayout","optional":false},{"name":"cfg","type":"{\n    forces: AgentSteer2[]\n    /** Excitement: effort above `rest` (in max-force units) charges by `gain`, and cools at `cool` per second. */\n    agitation: {rest: number; gain: number; cool: number}\n    /** Slowest cruise, as a fraction of each agent's top speed. */\n    cruiseFloor: number\n    /** Speed kept when an agent bounces off an edge (0–1). */\n    wallRestitution: number\n    name: string\n}","optional":false}]},"sim.agentForces.integrator.orientation2d":{"id":"sim.agentForces.integrator.orientation2d","name":"orientation2d","qualifiedName":"integrator.orientation2d","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"Agents that turn in place under summed torques and drift a little toward the field.","description":"","examples":["integrator.orientation2d(simLayout, {home: field.jitteredGridHome(simLayout, {overscan: 0.14}), fieldAt: field.dipoleOrRadial(simLayout), torques: [torque.alignToField(simLayout), torque.rest(simLayout)], maxOffset: 0.12, name: 'filingsUpdate'})"],"tips":[],"see":["sim.agentForces.torque","sim.agentForces.field"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.integrator.orientation2d"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":735},"kind":"function","signature":"integrator.orientation2d(layout: OrientationLayout, cfg: {\n    home: AgentHome2\n    fieldAt: AgentFieldAt\n    torques: AgentTorque[]\n    maxOffset: number\n    name: string\n}): (i: number) => void","params":[{"name":"layout","type":"OrientationLayout","optional":false},{"name":"cfg","type":"{\n    home: AgentHome2\n    fieldAt: AgentFieldAt\n    torques: AgentTorque[]\n    maxOffset: number\n    name: string\n}","optional":false}]},"sim.agentForces.integrator.drift2d":{"id":"sim.agentForces.integrator.drift2d","name":"drift2d","qualifiedName":"integrator.drift2d","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"Motes that glide on a built-in drift plus a fading gust, wrapping around the canvas.","description":"","examples":["integrator.drift2d(simLayout, {drift: drift.variedHeading(simLayout), gust: force.cursorGust(simLayout), name: 'motesUpdate'})"],"tips":[],"see":["sim.agentForces.drift.variedHeading","sim.agentForces.force.cursorGust"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.integrator.drift2d"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":855},"kind":"function","signature":"integrator.drift2d(layout: DriftLayout, cfg: {\n    drift: AgentDrift\n    gust: (pos: d.v2f) => d.v2f\n    name: string\n}): (i: number) => void","params":[{"name":"layout","type":"DriftLayout","optional":false},{"name":"cfg","type":"{\n    drift: AgentDrift\n    gust: (pos: d.v2f) => d.v2f\n    name: string\n}","optional":false}]},"sim.agentForces.integrator.relief":{"id":"sim.agentForces.integrator.relief","name":"relief","qualifiedName":"integrator.relief","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"Particles standing on the child's image, rising by a color channel, springing home.","description":"","examples":["integrator.relief(fieldLayout, {channel: channel.luminance, cursor: force.cursorInView(fieldLayout, {perspK: 0.9}), stiffness: 18, maxSpeed: 4, minAlpha: 0.02, name: 'reliefUpdate'})"],"tips":[],"see":["sim.agentForces.channel","sim.agentForces.force.cursorInView"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.integrator.relief"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":1074},"kind":"function","signature":"integrator.relief(layout: ReliefLayout, cfg: {\n    channel: AgentChannel\n    cursor: (homeW: d.v2f, off: d.v2f, z: number) => d.v3f\n    stiffness: number\n    maxSpeed: number\n    /** Child alpha below which a particle is never latched. Match the render's `minAlpha`. */\n    // Matches the splat's early-out.\n    minAlpha: number\n    name: string\n}): (x: number, y: number) => void","params":[{"name":"layout","type":"ReliefLayout","optional":false},{"name":"cfg","type":"{\n    channel: AgentChannel\n    cursor: (homeW: d.v2f, off: d.v2f, z: number) => d.v3f\n    stiffness: number\n    maxSpeed: number\n    /** Child alpha below which a particle is never latched. Match the render's `minAlpha`. */\n    // Matches the splat's early-out.\n    minAlpha: number\n    name: string\n}","optional":false}]},"sim.agentForces.integrator.advect2d":{"id":"sim.agentForces.integrator.advect2d","name":"advect2d","qualifiedName":"integrator.advect2d","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"Particles carried by a fluid's velocity texture, easing back to an even spread.","description":"","examples":["integrator.advect2d(pfLayout, {gridN: 256, inertiaRate: 6, homeRate: 0.6, name: 'particleFlowIntegrate'})"],"tips":[],"see":["sim.fluids.fluidSim"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.integrator.advect2d"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":1179},"kind":"function","signature":"integrator.advect2d(layout: AdvectLayout, cfg: {\n    gridN: number\n    inertiaRate: number\n    homeRate: number\n    name: string\n}): (i: number) => void","params":[{"name":"layout","type":"AdvectLayout","optional":false},{"name":"cfg","type":"{\n    gridN: number\n    inertiaRate: number\n    homeRate: number\n    name: string\n}","optional":false}]},"sim.agentForces.integrator":{"id":"sim.agentForces.integrator","name":"integrator","qualifiedName":"integrator","category":"sim.agentForces","module":"std/sim/agentForces.ts","summary":"The update step of an agent simulation: one per kind of agent.","description":"Each takes your layout and the parts, and returns the step to register as `update` in `bake`.\n\n`forces3d` moves a 3D swarm under summed forces with drag and a speed limit. `steering2d` moves a flock that keeps cruising and arcs off the edges. `orientation2d` turns agents in place under summed torques. `drift2d` glides motes and wraps them around the canvas. `relief` stands particles on the child's image. `advect2d` carries particles along a fluid.","examples":["const update = integrator.forces3d(simLayout, {forces: [pressure.force, force.containment(simLayout, containmentCfg), force.cursorXY(simLayout), force.gravity(simLayout)], maxSpeed: 3, posClamp: 1.9, name: 'swarmUpdate'})"],"tips":["Drag arrives as a per-frame multiplier in `params.dragMul`. Compute it with `agentFrame.expDecay(rate, dt)` so it does not depend on frame rate."],"see":["sim.agentForces.force","sim.agentForces.torque","sim.agentForces.drift","sim.agentForces.field","sim.agents.renderAgents","sim.agents.agentSim"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentForces.integrator"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentForces.ts","line":1457},"kind":"object","signature":"integrator: { forces3d, steering2d, orientation2d, drift2d, relief, advect2d }","members":["sim.agentForces.integrator.forces3d","sim.agentForces.integrator.steering2d","sim.agentForces.integrator.orientation2d","sim.agentForces.integrator.drift2d","sim.agentForces.integrator.relief","sim.agentForces.integrator.advect2d"],"params":[]},"sim.agentFrame.expDecay":{"id":"sim.agentFrame.expDecay","name":"expDecay","qualifiedName":"expDecay","category":"sim.agentFrame","module":"std/sim/agentFrame.ts","summary":"The per-frame multiplier that decays a value at `rate` per second, whatever the frame rate.","description":"Write it into a `dragMul` value each frame. A rate of 1 halves a velocity in about 0.7 s.","examples":["dragMul: agentFrame.expDecay(2.2 + damping * 6, dt)"],"tips":[],"see":["sim.agentForces.integrator"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentFrame.expDecay"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentFrame.ts","line":30},"kind":"function","signature":"expDecay(rate: number, dt: number): number","params":[{"name":"rate","type":"number","optional":false},{"name":"dt","type":"number","optional":false}]},"sim.agentFrame.pointerToShapeLocal":{"id":"sim.agentFrame.pointerToShapeLocal","name":"pointerToShapeLocal","qualifiedName":"pointerToShapeLocal","category":"sim.agentFrame","module":"std/sim/agentFrame.ts","summary":"The pointer in the shape's own coordinates: undo the layer's center, scale and rotation so a cursor force can act in the same space the swarm lives in (y up, isotropic).","description":"`pointerX`/`pointerY` and `centerX`/`centerYv` are in uv, `rotC`/`rotS` the cosine and sine of the rotation, `aspect` the canvas width over height.","examples":["const cursor = agentFrame.pointerToShapeLocal({pointerX, pointerY, centerX, centerYv, scale, rotC, rotS, aspect})"],"tips":[],"see":["sim.agentForces.force"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentFrame.pointerToShapeLocal"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentFrame.ts","line":51},"kind":"function","signature":"pointerToShapeLocal(opts: {\n    pointerX: number\n    pointerY: number\n    centerX: number\n    centerYv: number\n    scale: number\n    rotC: number\n    rotS: number\n    aspect: number\n}): {x: number; y: number}","params":[{"name":"opts","type":"{\n    pointerX: number\n    pointerY: number\n    centerX: number\n    centerYv: number\n    scale: number\n    rotC: number\n    rotS: number\n    aspect: number\n}","optional":false}]},"sim.agentFrame.omegaFromRotationDeltas":{"id":"sim.agentFrame.omegaFromRotationDeltas","name":"omegaFromRotationDeltas","qualifiedName":"omegaFromRotationDeltas","category":"sim.agentFrame","module":"std/sim/agentFrame.ts","summary":"How fast a rotating shape's surface moves, from its rotation this frame and last frame, so a swarm inside it can be dragged along.","description":"Angles in radians, `dt` in seconds. Null on the first frame.","examples":["om = agentFrame.omegaFromRotationDeltas(prevRot, next, dt) ?? om"],"tips":[],"see":["sim.agentFrame.entrainmentFromOmega"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentFrame.omegaFromRotationDeltas"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentFrame.ts","line":84},"kind":"function","signature":"omegaFromRotationDeltas(prev: {x: number; y: number; z: number} | null, next: {x: number; y: number; z: number}, dt: number): {x: number; y: number; z: number} | null","params":[{"name":"prev","type":"{x: number; y: number; z: number} | null","optional":false},{"name":"next","type":"{x: number; y: number; z: number}","optional":false},{"name":"dt","type":"number","optional":false}]},"sim.agentFrame.entrainmentFromOmega":{"id":"sim.agentFrame.entrainmentFromOmega","name":"entrainmentFromOmega","qualifiedName":"entrainmentFromOmega","category":"sim.agentFrame","module":"std/sim/agentFrame.ts","summary":"The values `force.containment` reads for a rotating shape: the rotation rate capped at `cap`, and an `entrain` gain that grows with the rate up to `gainMax`.","description":"","examples":["const {omegaX, omegaY, omegaZ, entrain} = agentFrame.entrainmentFromOmega(om, {cap: 10, gainRate: 2.5, gainMax: 5})"],"tips":["With a still shape the gain is exactly 0, so the entrainment force costs nothing to leave declared."],"see":["sim.agentFrame.omegaFromRotationDeltas","sim.agentForces.force"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentFrame.entrainmentFromOmega"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentFrame.ts","line":119},"kind":"function","signature":"entrainmentFromOmega(om: {x: number; y: number; z: number}, opts: {cap: number; gainRate: number; gainMax: number}): { omegaX: number; omegaY: number; omegaZ: number; entrain: number; }","params":[{"name":"om","type":"{x: number; y: number; z: number}","optional":false},{"name":"opts","type":"{cap: number; gainRate: number; gainMax: number}","optional":false}]},"sim.agentFrame.createMotionAxis":{"id":"sim.agentFrame.createMotionAxis","name":"createMotionAxis","qualifiedName":"createMotionAxis","category":"sim.agentFrame","module":"std/sim/agentFrame.ts","summary":"A unit direction that follows where the pointer has been travelling, smoothed frame to frame.","description":"Feed `update(x, y)` the pointer each frame and write the result into an axis value.\n\n`smoothing` (0–1, default 0.1) is how fast the axis turns. A jump longer than `teleport` (default 0.25, in the same units as the pointer) is the cursor re-entering the canvas, not a stroke, and leaves the axis alone.","examples":["const motionAxis = agentFrame.createMotionAxis({smoothing: 0.1, teleport: 0.25})\nconst axis = motionAxis.update(cursorX, cursorY)"],"tips":[],"see":["sim.agentForces.field"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentFrame.createMotionAxis"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentFrame.ts","line":147},"kind":"function","signature":"createMotionAxis(opts?: {smoothing?: number; teleport?: number}): { update(x: number, y: number): { x: number; y: number; }; }","params":[{"name":"opts","type":"{smoothing?: number; teleport?: number}","optional":true}]},"sim.agentFrame.fitJitteredGrid":{"id":"sim.agentFrame.fitJitteredGrid","name":"fitJitteredGrid","qualifiedName":"fitJitteredGrid","category":"sim.agentFrame","module":"std/sim/agentFrame.ts","summary":"A grid of about `count` roughly square cells over the canvas plus an `overscan` border on every side, for agents that rest on a jittered grid.","description":"`domainX` is the canvas width in heights (the aspect).\n\nReturns the column count and the cell size, which `field.jitteredGridHome` reads.","examples":["const {cols, cellW, cellH} = agentFrame.fitJitteredGrid(count, domainX, 0.1)"],"tips":[],"see":["sim.agentFrame.fitIsoGrid","sim.agentForces.field"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentFrame.fitJitteredGrid"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentFrame.ts","line":191},"kind":"function","signature":"fitJitteredGrid(count: number, domainX: number, overscan: number): { cols: number; cellW: number; cellH: number; }","params":[{"name":"count","type":"number","optional":false},{"name":"domainX","type":"number","optional":false},{"name":"overscan","type":"number","optional":false}]},"sim.agentFrame.fitIsoGrid":{"id":"sim.agentFrame.fitIsoGrid","name":"fitIsoGrid","qualifiedName":"fitIsoGrid","category":"sim.agentFrame","module":"std/sim/agentFrame.ts","summary":"A W×H grid of square cells holding about `count` agents at the canvas `aspect`, each side clamped to `[min, max]`.","description":"","examples":["const {w, h} = agentFrame.fitIsoGrid(count, aspect, 8, 160)"],"tips":[],"see":["sim.agentFrame.fitJitteredGrid"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentFrame.fitIsoGrid"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentFrame.ts","line":209},"kind":"function","signature":"fitIsoGrid(count: number, aspect: number, min: number, max: number): { w: number; h: number; }","params":[{"name":"count","type":"number","optional":false},{"name":"aspect","type":"number","optional":false},{"name":"min","type":"number","optional":false},{"name":"max","type":"number","optional":false}]},"sim.agentFrame.cameraRowsYDown":{"id":"sim.agentFrame.cameraRowsYDown","name":"cameraRowsYDown","qualifiedName":"cameraRowsYDown","category":"sim.agentFrame","module":"std/sim/agentFrame.ts","summary":"The three rows of a camera rotation for a 3D view drawn on a y-down canvas.","description":"Angles in radians. Write them into the values a projected splat reads.\n\nPositive angles orbit in the directions that feel natural in the editor.","examples":["const rows = agentFrame.cameraRowsYDown(rotX * DEG_TO_RAD, rotY * DEG_TO_RAD, rotZ * DEG_TO_RAD)"],"tips":[],"see":["sim.agents.renderAgents"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentFrame.cameraRowsYDown"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentFrame.ts","line":233},"kind":"function","signature":"cameraRowsYDown(rx: number, ry: number, rz: number): {\n    rowX: [number, number, number]\n    rowY: [number, number, number]\n    rowZ: [number, number, number]\n}","params":[{"name":"rx","type":"number","optional":false},{"name":"ry","type":"number","optional":false},{"name":"rz","type":"number","optional":false}]},"sim.agentFrame.r3SubCellOffset":{"id":"sim.agentFrame.r3SubCellOffset","name":"r3SubCellOffset","qualifiedName":"r3SubCellOffset","category":"sim.agentFrame","module":"std/sim/agentFrame.ts","summary":"A tiny offset for frame `frameIdx`, within one grid `cell`, that `force.pressure` reads to keep its density grid from lining up with the shape.","description":"Increment the frame index each frame.","examples":["const gridOff = agentFrame.r3SubCellOffset(frameIdx++, cellSize)"],"tips":[],"see":["sim.agentForces.force"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentFrame.r3SubCellOffset"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentFrame.ts","line":266},"kind":"function","signature":"r3SubCellOffset(frameIdx: number, cell: number): {x: number; y: number; z: number}","params":[{"name":"frameIdx","type":"number","optional":false},{"name":"cell","type":"number","optional":false}]},"sim.agentRender.pointSlot.orbitalPlace":{"id":"sim.agentRender.pointSlot.orbitalPlace","name":"orbitalPlace","qualifiedName":"pointSlot.orbitalPlace","category":"sim.agentRender","module":"std/sim/agentRender.ts","summary":"The mote's drawn position: its stored position plus a slow circle of `radius` (canvas heights) at `rate` turns per second, both scaled by `params.randomness` and varied per mote.","description":"Reads `params.time`.","examples":["place: pointSlot.orbitalPlace(simLayout, {radius: 0.035, rate: 1.4})"],"tips":[],"see":["sim.agentRender.pointSlot.twinkleBrightness","sim.agentRender.pointSlot.presenceRadius"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentRender.pointSlot.orbitalPlace"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentRender.ts","line":265},"kind":"function","signature":"pointSlot.orbitalPlace(layout: {readonly $: {readonly params: {readonly time: number; readonly randomness: number}}}, cfg: {radius: number; rate: number}): PointWorldSplatConfig['place']","params":[{"name":"layout","type":"{readonly $: {readonly params: {readonly time: number; readonly randomness: number}}}","optional":false},{"name":"cfg","type":"{radius: number; rate: number}","optional":false}]},"sim.agentRender.pointSlot.twinkleBrightness":{"id":"sim.agentRender.pointSlot.twinkleBrightness","name":"twinkleBrightness","qualifiedName":"pointSlot.twinkleBrightness","category":"sim.agentRender","module":"std/sim/agentRender.ts","summary":"The mote's brightness: a twinkle at `freq` cycles per second, blended in by `params.twinkle` (0 steady, 1 full shimmer), times a per-mote presence so some motes read nearer than others.","description":"Reads `params.time`.","examples":["brightness: pointSlot.twinkleBrightness(simLayout, {freq: 2})"],"tips":[],"see":["sim.agentRender.pointSlot.orbitalPlace","sim.agentRender.pointSlot.presenceRadius"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentRender.pointSlot.twinkleBrightness"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentRender.ts","line":296},"kind":"function","signature":"pointSlot.twinkleBrightness(layout: {readonly $: {readonly params: {readonly time: number; readonly twinkle: number}}}, cfg: {freq: number}): PointWorldSplatConfig['brightness']","params":[{"name":"layout","type":"{readonly $: {readonly params: {readonly time: number; readonly twinkle: number}}}","optional":false},{"name":"cfg","type":"{freq: number}","optional":false}]},"sim.agentRender.pointSlot.presenceRadius":{"id":"sim.agentRender.pointSlot.presenceRadius","name":"presenceRadius","qualifiedName":"pointSlot.presenceRadius","category":"sim.agentRender","module":"std/sim/agentRender.ts","summary":"The mote's size: `params.bodyR` scaled by the same per-mote presence as the brightness, so the motes that read nearer are bigger and brighter together.","description":"","examples":["bodyRadius: pointSlot.presenceRadius(simLayout)"],"tips":[],"see":["sim.agentRender.pointSlot.twinkleBrightness"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentRender.pointSlot.presenceRadius"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentRender.ts","line":322},"kind":"function","signature":"pointSlot.presenceRadius(layout: {readonly $: {readonly params: {readonly bodyR: number}}}): PointWorldSplatConfig['bodyRadius']","params":[{"name":"layout","type":"{readonly $: {readonly params: {readonly bodyR: number}}}","optional":false}]},"sim.agentRender.pointSlot":{"id":"sim.agentRender.pointSlot","name":"pointSlot","qualifiedName":"pointSlot","category":"sim.agentRender","module":"std/sim/agentRender.ts","summary":"The three parts a round mote is drawn with: where it sits (`orbitalPlace`), how bright it is (`twinkleBrightness`) and how big it is (`presenceRadius`).","description":"Each is built over your layout and handed to `renderAgents.pointWorld`.\n\nEvery part varies per mote by a hash of its index, so a field of motes never moves in step.","examples":["renderAgents.pointWorld(simLayout, {shape, res: 1024, place: pointSlot.orbitalPlace(simLayout, {radius: 0.035, rate: 1.4}), brightness: pointSlot.twinkleBrightness(simLayout, {freq: 2}), bodyRadius: pointSlot.presenceRadius(simLayout), names: {splat: 'motesSplat', resolve: 'motesResolve'}})"],"tips":[],"see":["sim.agents.renderAgents","sim.agentRender.splat"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentRender.pointSlot"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentRender.ts","line":252},"kind":"object","signature":"pointSlot: { orbitalPlace, twinkleBrightness, presenceRadius }","members":["sim.agentRender.pointSlot.orbitalPlace","sim.agentRender.pointSlot.twinkleBrightness","sim.agentRender.pointSlot.presenceRadius"],"params":[]},"sim.agentRender.splat.orientedWorld":{"id":"sim.agentRender.splat.orientedWorld","name":"orientedWorld","qualifiedName":"splat.orientedWorld","category":"sim.agentRender","module":"std/sim/agentRender.ts","summary":"Agents with a heading: arrows, streaks, squares, dots or comets drawn along the way they point.","description":"","examples":["splat: {kernel: splat.orientedWorld(simLayout, {shape: 'arrow', res: 1024, splatRCap: 16, heading: 'velocity', agitation: 'buffer', comet: true, name: 'boidsSplat'}), threads: 'agents'}"],"tips":[],"see":["sim.agentRender.resolve.ramp","sim.agents.renderAgents.orientedWorld"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentRender.splat.orientedWorld"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentRender.ts","line":902},"kind":"function","signature":"splat.orientedWorld: (layout: WorldSplatLayout, cfg: OrientedWorldSplatConfig) => (i: number) => void","params":[]},"sim.agentRender.splat.pointWorld":{"id":"sim.agentRender.splat.pointWorld","name":"pointWorld","qualifiedName":"splat.pointWorld","category":"sim.agentRender","module":"std/sim/agentRender.ts","summary":"Round motes with a live softness, placed, brightened and sized by `pointSlot` parts.","description":"","examples":["splat: {kernel: splat.pointWorld(simLayout, {shape: 'glow', res: 1024, place: pointSlot.orbitalPlace(simLayout, {radius: 0.035, rate: 1.4}), brightness: pointSlot.twinkleBrightness(simLayout, {freq: 2}), bodyRadius: pointSlot.presenceRadius(simLayout), name: 'motesSplat'}), threads: 'agents'}"],"tips":[],"see":["sim.agentRender.resolve.tint","sim.agentRender.pointSlot","sim.agents.renderAgents.pointWorld"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentRender.splat.pointWorld"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentRender.ts","line":912},"kind":"function","signature":"splat.pointWorld: (layout: PointWorldSplatLayout, cfg: PointWorldSplatConfig) => (i: number) => void","params":[]},"sim.agentRender.splat.volume":{"id":"sim.agentRender.splat.volume","name":"volume","qualifiedName":"splat.volume","category":"sim.agentRender","module":"std/sim/agentRender.ts","summary":"A 3D swarm in perspective, recording speed for the ramp resolve.","description":"","examples":["splat: {kernel: splat.volume(simLayout, {shape: 'dot', outRes: 1024, maxSplatSize: 6, extQ: 2.6, names: {splat: 'swarmSplat', glow: 'swarmGlowProfile', profile: 'swarmProfile'}}), threads: 'agents'}"],"tips":[],"see":["sim.agentRender.resolve.ramp","sim.agents.renderAgents.volume"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentRender.splat.volume"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentRender.ts","line":922},"kind":"function","signature":"splat.volume: (layout: VolumeRenderLayout, cfg: VolumeSplatConfig) => (i: number) => void","params":[]},"sim.agentRender.splat.relief":{"id":"sim.agentRender.splat.relief","name":"relief","qualifiedName":"splat.relief","category":"sim.agentRender","module":"std/sim/agentRender.ts","summary":"Particles standing on an image, drawn in the child's color through a camera.","description":"Dispatch it over a 2D grid.","examples":["splat: {kernel: splat.relief(fieldLayout, {shape: 'dot', splatRCap: 24, aaW: 0.75, minAlpha: 0.02, fp: 1024, wobbleFreq: 1.3, perspK: 0.9, name: 'reliefSplat'}), threads: 'grid'}"],"tips":[],"see":["sim.agentRender.resolve.weightedColor","sim.agents.renderAgents.relief"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentRender.splat.relief"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentRender.ts","line":933},"kind":"function","signature":"splat.relief: (layout: ReliefRenderLayout, cfg: ReliefSplatConfig) => (x: number, y: number) => void","params":[]},"sim.agentRender.splat":{"id":"sim.agentRender.splat","name":"splat","qualifiedName":"splat","category":"sim.agentRender","module":"std/sim/agentRender.ts","summary":"The step that draws every agent into the shared canvas, one variant per kind of agent.","description":"`renderAgents` pairs each with its resolve; reach for `splat` alone when you need another pairing.","examples":["splat: {kernel: splat.orientedWorld(simLayout, {shape: 'arrow', res: 1024, splatRCap: 16, heading: 'velocity', agitation: 'buffer', comet: true, name: 'boidsSplat'}), threads: 'agents'}"],"tips":["`relief` dispatches over a 2D grid (`threads: 'grid'`); the other three dispatch over `agents`."],"see":["sim.agentRender.resolve","sim.agents.renderAgents","sim.agentRender.pointSlot"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentRender.splat"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentRender.ts","line":891},"kind":"object","signature":"splat: { orientedWorld, pointWorld, volume, relief }","members":["sim.agentRender.splat.orientedWorld","sim.agentRender.splat.pointWorld","sim.agentRender.splat.volume","sim.agentRender.splat.relief"],"params":[]},"sim.agentRender.resolve.ramp":{"id":"sim.agentRender.resolve.ramp","name":"ramp","qualifiedName":"resolve.ramp","category":"sim.agentRender","module":"std/sim/agentRender.ts","summary":"Rest-to-excited color from two colors, with optional motion trails.","description":"","examples":["resolve: {kernel: resolve.ramp(simLayout, {colorSpace: 2, res: 1024, invGain: 1 / 255, trails: 'on', name: 'boidsResolve'}), threads: 'fixed', size: [1024, 1024]}"],"tips":[],"see":["sim.agentRender.splat.orientedWorld","sim.agentRender.splat.volume"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentRender.resolve.ramp"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentRender.ts","line":958},"kind":"function","signature":"resolve.ramp: (layout: RampResolveLayout, cfg: RampResolveConfig) => (x: number, y: number) => void","params":[]},"sim.agentRender.resolve.tint":{"id":"sim.agentRender.resolve.tint","name":"tint","qualifiedName":"resolve.tint","category":"sim.agentRender","module":"std/sim/agentRender.ts","summary":"One color, with an alpha from coverage.","description":"","examples":["resolve: {kernel: resolve.tint(simLayout, {res: 1024, name: 'motesResolve'}), threads: 'fixed', size: [1024, 1024]}"],"tips":[],"see":["sim.agentRender.splat.pointWorld"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentRender.resolve.tint"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentRender.ts","line":968},"kind":"function","signature":"resolve.tint: (layout: { readonly $: { readonly accumE: atomicU32[]; readonly outTex: StorageTex; readonly params: { readonly color: v4f; }; }; }, cfg: { res: number; name: string; }) => (x: number, y: number) => void","params":[]},"sim.agentRender.resolve.weightedColor":{"id":"sim.agentRender.resolve.weightedColor","name":"weightedColor","qualifiedName":"resolve.weightedColor","category":"sim.agentRender","module":"std/sim/agentRender.ts","summary":"The average of the colors that landed, nearer particles first.","description":"","examples":["resolve: {kernel: resolve.weightedColor(fieldLayout, {fp: 1024, alphaK: 1.6, name: 'reliefResolve'}), threads: 'fixed', size: [outW, outH]}"],"tips":[],"see":["sim.agentRender.splat.relief"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentRender.resolve.weightedColor"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentRender.ts","line":978},"kind":"function","signature":"resolve.weightedColor: (layout: ReliefRenderLayout, cfg: { fp: number; alphaK: number; name: string; }) => (x: number, y: number) => void","params":[]},"sim.agentRender.resolve":{"id":"sim.agentRender.resolve","name":"resolve","qualifiedName":"resolve","category":"sim.agentRender","module":"std/sim/agentRender.ts","summary":"The step that turns the shared canvas into the output picture and clears it for the next frame.","description":"Pair `ramp` with `orientedWorld` or `volume`, `tint` with `pointWorld`, `weightedColor` with `relief`.","examples":["resolve: {kernel: resolve.tint(simLayout, {res: 1024, name: 'motesResolve'}), threads: 'fixed', size: [1024, 1024]}"],"tips":["Dispatch a resolve with `threads: 'fixed'` over the canvas size. It must touch every texel to clear it."],"see":["sim.agentRender.splat","sim.agents.renderAgents"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.agentRender.resolve"},"usedBy":[],"documented":true,"source":{"file":"std/sim/agentRender.ts","line":948},"kind":"object","signature":"resolve: { ramp, tint, weightedColor }","members":["sim.agentRender.resolve.ramp","sim.agentRender.resolve.tint","sim.agentRender.resolve.weightedColor"],"params":[]},"sim.shapeFields.shapeField.analyticExtruded":{"id":"sim.shapeFields.shapeField.analyticExtruded","name":"analyticExtruded","qualifiedName":"shapeField.analyticExtruded","category":"sim.shapeFields","module":"std/sim/shapeFields.ts","summary":"A flat shape given depth: the 2D distance of a circle, polygon or star, extruded in z by `params.halfDepth`.","description":"The shape's sub-props are read live from `params.sa*`.","examples":["shapeField.analyticExtruded(simLayout, buildAnalyticSdfFn('hexagon'))"],"tips":[],"see":["sim.shapeFields.shapeField.svgExtruded","sim.shapeFields.shapeField.analytic3d"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.shapeFields.shapeField.analyticExtruded"},"usedBy":[],"documented":true,"source":{"file":"std/sim/shapeFields.ts","line":67},"kind":"function","signature":"shapeField.analyticExtruded(layout: {readonly $: {readonly params: ExtrudedAnalyticParamsView}}, sdf2d: AnalyticSdf2d): ShapeField3","params":[{"name":"layout","type":"{readonly $: {readonly params: ExtrudedAnalyticParamsView}}","optional":false},{"name":"sdf2d","type":"AnalyticSdf2d","optional":false}]},"sim.shapeFields.shapeField.svgExtruded":{"id":"sim.shapeFields.shapeField.svgExtruded","name":"svgExtruded","qualifiedName":"shapeField.svgExtruded","category":"sim.shapeFields","module":"std/sim/shapeFields.ts","summary":"An SVG outline given depth: the outline's distance texture, extruded in z by `params.halfDepth`.","description":"`size` is the texture's side in texels.","examples":["shapeField.svgExtruded(simLayout, svgSdfLayout, {size: 512})"],"tips":["Outside the texture's footprint the distance keeps growing, so strays still find their way back."],"see":["sim.shapeFields.shapeField.analyticExtruded","sim.shapeFields.shapeField.svgLifted3d"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.shapeFields.shapeField.svgExtruded"},"usedBy":[],"documented":true,"source":{"file":"std/sim/shapeFields.ts","line":87},"kind":"function","signature":"shapeField.svgExtruded(layout: {readonly $: {readonly params: {readonly halfDepth: number}}}, svgLayout: {readonly $: {readonly sdfSource: d.Infer<d.WgslTexture2d<d.F32>>}}, cfg: {size: number}): ShapeField3","params":[{"name":"layout","type":"{readonly $: {readonly params: {readonly halfDepth: number}}}","optional":false},{"name":"svgLayout","type":"{readonly $: {readonly sdfSource: d.Infer<d.WgslTexture2d<d.F32>>}}","optional":false},{"name":"cfg","type":"{size: number}","optional":false}]},"sim.shapeFields.shapeField.analytic3d":{"id":"sim.shapeFields.shapeField.analytic3d","name":"analytic3d","qualifiedName":"shapeField.analytic3d","category":"sim.shapeFields","module":"std/sim/shapeFields.ts","summary":"A true 3D solid (sphere, torus, knot…) from its baked distance function, rotated by the shape's live rotation so the swarm follows the surface as it turns.","description":"","examples":["shapeField.analytic3d(setup3d.layout, setup3d.sdfFn as ShapeField3)"],"tips":[],"see":["sim.shapeFields.shapeField.analyticExtruded","sim.shapeFields.shapeField.svgLifted3d"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.shapeFields.shapeField.analytic3d"},"usedBy":[],"documented":true,"source":{"file":"std/sim/shapeFields.ts","line":112},"kind":"function","signature":"shapeField.analytic3d(layout: RotatedFieldLayout, baked: ShapeField3): ShapeField3","params":[{"name":"layout","type":"RotatedFieldLayout","optional":false},{"name":"baked","type":"ShapeField3","optional":false}]},"sim.shapeFields.shapeField.svgLifted3d":{"id":"sim.shapeFields.shapeField.svgLifted3d","name":"svgLifted3d","qualifiedName":"shapeField.svgLifted3d","category":"sim.shapeFields","module":"std/sim/shapeFields.ts","summary":"An SVG outline lifted into a 3D solid, rotated by the shape's live rotation.","description":"","examples":["shapeField.svgLifted3d(setup3d.layout, setup3d.sdfFn as ShapeField3)"],"tips":[],"see":["sim.shapeFields.shapeField.svgExtruded","sim.shapeFields.shapeField.analytic3d"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.shapeFields.shapeField.svgLifted3d"},"usedBy":[],"documented":true,"source":{"file":"std/sim/shapeFields.ts","line":125},"kind":"function","signature":"shapeField.svgLifted3d(layout: RotatedFieldLayout, baked: ShapeField3): ShapeField3","params":[{"name":"layout","type":"RotatedFieldLayout","optional":false},{"name":"baked","type":"ShapeField3","optional":false}]},"sim.shapeFields.shapeField":{"id":"sim.shapeFields.shapeField","name":"shapeField","qualifiedName":"shapeField","category":"sim.shapeFields","module":"std/sim/shapeFields.ts","summary":"The signed-distance function of the shape a swarm lives in, for `force.containment`.","description":"Four builders, one per shape source. Each returns a `ShapeField3`: a GPU function from a shape-local 3D point to a signed distance, negative inside. `analyticExtruded` takes a flat shape (circle, polygon, star…) and gives it a depth from `params.halfDepth`. `svgExtruded` does the same for an SVG outline uploaded as a distance texture. `analytic3d` wraps a true 3D solid so it rotates under the swarm. `svgLifted3d` wraps an SVG lifted into 3D.","examples":["force.containment(simLayout, {field: shapeField.analyticExtruded(simLayout, buildAnalyticSdfFn(shapeType)), gradEps: 0.02, wall: {featherFrom: -0.1, featherTo: 0.01, base: 1.1, springK: 22}, recall: {from: 1.5, to: 1.9, k: 9}, homing: {from: 0.12, to: 0.45, k: 5}, entrainment: {featherHalf: 0.05}})"],"tips":["Pick the builder at bake time from the shape props and hand ONE field to the integrator. The swarm never needs to know which kind it is."],"see":["sim.agentForces.force","sim.agentForces.integrator"],"import":{"statement":"import {sim} from 'shaders/std'","usage":"sim.shapeFields.shapeField"},"usedBy":[],"documented":true,"source":{"file":"std/sim/shapeFields.ts","line":151},"kind":"object","signature":"shapeField: { analyticExtruded, svgExtruded, analytic3d, svgLifted3d }","members":["sim.shapeFields.shapeField.analyticExtruded","sim.shapeFields.shapeField.svgExtruded","sim.shapeFields.shapeField.analytic3d","sim.shapeFields.shapeField.svgLifted3d"],"params":[]}},"types":{"define.WgslType":{"id":"define.WgslType","name":"WgslType","category":"define","module":"std/wgsl.ts","kind":"type","text":"type WgslType = 'f32' | 'vec2f' | 'vec3f' | 'vec4f' | 'texture_2d<f32>' | 'sampler'","summary":"The WGSL types a bound name can have inside a body.","description":"","source":{"file":"std/wgsl.ts","line":51}},"define.WgslInput":{"id":"define.WgslInput","name":"WgslInput","category":"define","module":"std/wgsl.ts","kind":"type","text":"type WgslInput = ArgSpec | {readonly value: ArgSpec; readonly type: WgslType}","summary":"One entry of `inputs`: the value to bind under a name — a prop `p('x')`, a context token `ctx.time`, a `Scalar`, or a number — optionally with its WGSL type spelled out as `{value, type}`.","description":"A prop takes its own type; a `Scalar` or a number is `f32`.","source":{"file":"std/wgsl.ts","line":58}},"define.WgslSpec":{"id":"define.WgslSpec","name":"WgslSpec","category":"define","module":"std/wgsl.ts","kind":"interface","text":"interface WgslSpec {\n    /** The WGSL statements. Must `return` a `vec4f` color. */\n    readonly body: string\n    /** Extra names to bind, on top of the props and context values bound automatically. */\n    readonly inputs?: Record<string, WgslInput>\n    /**\n     * Gather filters only: what alpha the returned color is in. `'premultiplied'` (default)\n     * lets the engine unpremultiply it for you; `'straight'` returns it as-is.\n     */\n    readonly alpha?: 'premultiplied' | 'straight'\n    /** A readable name for the generated WGSL function. Defaults to the definition's name. */\n    readonly name?: string\n}","summary":"The long form of a body: `wgsl({body, inputs, alpha, name})`.","description":"","source":{"file":"std/wgsl.ts","line":61}},"define.WgslHost":{"id":"define.WgslHost","name":"WgslHost","category":"define","module":"std/wgsl.ts","kind":"type","text":"type WgslHost = 'generator' | 'pointwise' | 'gather'","summary":"Where a body runs: a generator's `paint:`, or a pointwise or gather filter's `effect:`.","description":"","source":{"file":"std/wgsl.ts","line":253}},"define.RecompileRule":{"id":"define.RecompileRule","name":"RecompileRule","category":"define","module":"std/types.ts","kind":"type","text":"type RecompileRule =\n    | {readonly kind: 'crosses'; readonly value: number}\n    // The loose fn signature mirrors PropConfig.compileTimeWhen.\n    | {readonly kind: 'custom'; readonly predicate: (prev: any, next: any) => boolean}","summary":"When a prop change rebuilds the shader: `crosses(value)` at a threshold, or `recompileWhen(fn)` by your own test.","description":"","source":{"file":"std/types.ts","line":31}},"define.StdPropConfig":{"id":"define.StdPropConfig","name":"StdPropConfig","category":"define","module":"std/types.ts","kind":"type","text":"type StdPropConfig<V> = Omit<PropConfig<V>, 'compileTimeWhen'> & {\n    recompile?: RecompileRule\n}","summary":"One prop of a definition: `default`, optional `transform`, `ui`, `description`, `compileTime`, and a `recompile:` rule.","description":"","source":{"file":"std/types.ts","line":41}},"define.StdProps":{"id":"define.StdProps","name":"StdProps","category":"define","module":"std/types.ts","kind":"type","text":"type StdProps<T extends ComponentProps> = {[K in keyof T]: StdPropConfig<T[K]>}","summary":"The `props` object of a definition: one `StdPropConfig` per prop name.","description":"","source":{"file":"std/types.ts","line":46}},"define.IdentityRule":{"id":"define.IdentityRule","name":"IdentityRule","category":"define","module":"std/types.ts","kind":"type","text":"type IdentityRule =\n    | {readonly kind: 'isZero'; readonly prop: string}\n    | {readonly kind: 'isValue'; readonly prop: string; readonly value: unknown}\n    | {readonly kind: 'allOf'; readonly rules: IdentityRule[]}\n    | {readonly kind: 'custom'; readonly props: string[]; readonly when: (values: Record<string, unknown>) => boolean}","summary":"When a filter does nothing and can be skipped: built with `isZero`, `isValue`, `allOf` or `identityWhenever`.","description":"","source":{"file":"std/types.ts","line":55}},"define.PointwiseEffect":{"id":"define.PointwiseEffect","name":"PointwiseEffect","category":"define","module":"std/types.ts","kind":"interface","text":"interface PointwiseEffect {\n    readonly kind: 'pointwise'\n    readonly body?: {fn: unknown; hint: string} | ((propValues: Record<string, unknown>) => {fn: unknown; hint: string})\n    /** Build the new color yourself from `params.childNode`. Give exactly one of `body` or `build`. */\n    readonly build?: (params: FilterParams) => Expr\n    /** The values that follow the child's color as `body`'s arguments. */\n    readonly args?: (params: FilterParams) => Expr[]\n    /** Post-process the result (a color-space mix, a palette lookup). */\n    readonly compose?: (result: Expr, params: FilterParams) => Expr\n    /** Runs once per build, only when the filter is not skipped (for per-frame driven values). */\n    readonly setup?: (params: FilterParams) => void\n}","summary":"A filter that edits the layer inside one pixel at a time: what the color words (`saturate`, `hueRotate`, …) and `pointwise({build})` return.","description":"Give either `body` (a compiled GPU function taking the child's color first, then `args`) or `build` (a function of the params that returns the new color from `params.childNode`). The child arrives in straight alpha and there is no extra render pass.","source":{"file":"std/types.ts","line":72}},"define.GatherEffect":{"id":"define.GatherEffect","name":"GatherEffect","category":"define","module":"std/types.ts","kind":"interface","text":"interface GatherEffect {\n    readonly kind: 'gather'\n    /** Build the new color from the child texture. */\n    readonly build: (params: RttFilterParams) => Expr\n    readonly resultAlpha?: 'premultiplied' | 'straight'\n    readonly setup?: (params: RttFilterParams) => void\n}","summary":"A filter that reads the layer inside anywhere, not just at this pixel: blurs, halftones, displacements.","description":"The child is rendered to a texture first (`params.texture`, premultiplied) and `params.sampleStraight(uv)` reads it back in straight alpha. Return premultiplied color and it is unpremultiplied for you; say `resultAlpha: 'straight'` when you already did.","source":{"file":"std/types.ts","line":94}},"define.DisplaceByEffectRef":{"id":"define.DisplaceByEffectRef","name":"DisplaceByEffectRef","category":"define","module":"std/types.ts","kind":"interface","text":"interface DisplaceByEffectRef {\n    readonly kind: 'displaceBy'\n}","summary":"A filter that moves the layer inside by a simulation's vector field.","description":"See `displaceBy`.","source":{"file":"std/types.ts","line":103}},"define.StdDefinitionBase":{"id":"define.StdDefinitionBase","name":"StdDefinitionBase","category":"define","module":"std/types.ts","kind":"interface","text":"interface StdDefinitionBase<T extends ComponentProps> {\n    name: string\n    category?: string\n    description?: string\n    deprecatedNames?: string[]\n    boundingBoxDeclaration?: BoundingBoxDeclaration\n    props: StdProps<T>\n\n    // Declarative engine contract (passthrough).\n    usesPointer?: boolean\n    acceptsUVContext?: boolean\n    acceptsOptionalChild?: boolean\n    blendWithChildren?: boolean\n    capturesDOM?: boolean\n    wantsBoundsParams?: boolean\n    providesUVContextViaCompute?: boolean\n    naturalSizeKey?: {fromProp: string} | {fixed: string}\n    experimental?: ComponentProps\n    animatedTime?: {speed: string}\n    extraAnimatedTimes?: Record<string, string>\n    extraFields?: Record<string, {schema: import('typegpu/data').AnyWgslData; initial: number | number[]}>\n\n    /** A compute pass that runs before the pixel work (simulation state, a prepass). */\n    compute?: GpuComputeNode\n    /** Where a per-prop map driver samples its map (cell-centre sampling, for grids). */\n    mapSampleUVs?: GpuMapSampleUVs\n}","summary":"The fields every definition shares: `name`, `props`, optional `description` and `category`, the `animatedTime` clock, and engine flags.","description":"`animatedTime: {speed: 'speed'}` gives the layer its own clock scaled by that prop. `usesPointer` asks for mouse tracking, `acceptsOptionalChild` lets a generator take a nested layer, `extraFields` declares per-frame values you compute on the CPU. Most definitions need only the first three fields.","source":{"file":"std/types.ts","line":120}},"define.StdPointwiseFilterDefinition":{"id":"define.StdPointwiseFilterDefinition","name":"StdPointwiseFilterDefinition","category":"define","module":"std/types.ts","kind":"interface","text":"interface StdPointwiseFilterDefinition<T extends ComponentProps> extends StdDefinitionBase<T> {\n    role?: 'filter'\n    species?: 'pointwise'\n    effect: PointwiseEffect | import('./filter').TintTowardEffect | WgslBody\n    /** Skip the filter while this rule holds (`isZero`, `isValue`, `allOf`, `identityWhenever`). */\n    identityWhen?: IdentityRule\n    /** Logged when nothing is nested inside. Omit to stay silent (the output is transparent). */\n    missingChildMessage?: string\n}","summary":"A filter that edits the layer inside one pixel at a time: `effect:` holds a color word, `tintToward`, `pointwise({…})` or a `wgsl` body that reads `child`.","description":"`role` and `species` are optional; the `effect:` field already says filter, and its kind says pointwise. `identityWhen` skips the filter while a rule holds. `missingChildMessage` is shown when nothing is nested inside; omit it to fail silently with a transparent output.","source":{"file":"std/types.ts","line":155}},"define.StdGatherFilterDefinition":{"id":"define.StdGatherFilterDefinition","name":"StdGatherFilterDefinition","category":"define","module":"std/types.ts","kind":"interface","text":"interface StdGatherFilterDefinition<T extends ComponentProps> extends StdDefinitionBase<T> {\n    role?: 'filter'\n    species?: 'gather'\n    effect: GatherEffect | import('./filter').DisplaceByEffect | WgslBody\n    identityWhen?: IdentityRule\n    missingChildMessage?: string\n}","summary":"A filter that reads the layer inside anywhere: `effect:` holds `gather({…})`, `displaceBy(…)` or a `wgsl` body that samples `childTexture`.","description":"Same optional fields as the pointwise form. The child gets its own render pass so the filter can read neighbouring pixels.","source":{"file":"std/types.ts","line":171}},"define.StdWgslFilterDefinition":{"id":"define.StdWgslFilterDefinition","name":"StdWgslFilterDefinition","category":"define","module":"std/types.ts","kind":"interface","text":"interface StdWgslFilterDefinition<T extends ComponentProps> extends StdDefinitionBase<T> {\n    role?: 'filter'\n    species?: undefined\n    effect: WgslBody\n    identityWhen?: IdentityRule\n    missingChildMessage?: string\n}","summary":"A filter whose `effect:` is a `wgsl` body with no `species` declared: gather when the body samples `childTexture`, pointwise otherwise.","description":"Declare `species` to override what the body implies.","source":{"file":"std/types.ts","line":184}},"define.StdWarpDefinition":{"id":"define.StdWarpDefinition","name":"StdWarpDefinition","category":"define","module":"std/types.ts","kind":"interface","text":"interface StdWarpDefinition<T extends ComponentProps> extends StdDefinitionBase<T> {\n    role?: 'warp'\n    map: UvMapSource\n    /** Past the edge: `'prop'` (an `edges` prop, default), a fixed mode number, or `'none'`. */\n    edges?: UvRemapEdgeSource\n    /** How the layer is read back: `'catmullRom'` (default, sharp when magnified) or `'bilinear'`. */\n    resample?: 'catmullRom' | 'bilinear'\n    /** Treat the map as identity while this returns true (a texture it needs may not exist yet). */\n    uvRemapIdentityWhen?: (params: UvRemapHookParams) => boolean\n    missingChildMessage?: string\n}","summary":"A distortion: `map:` is one function from the pixel's coordinate to where in the layer inside to read.","description":"The map returns the source uv, or `{uv, coverage}` when parts of the output should be transparent. `edges` says what happens past the layer's edge (`'prop'` reads an `edges` prop, a number fixes a mode, `'none'` skips it). `resample` picks the filter used to read the layer (`'catmullRom'` by default, `'bilinear'` for blocky or many-tap maps).","source":{"file":"std/types.ts","line":202}},"define.StdShapeDefinition":{"id":"define.StdShapeDefinition","name":"StdShapeDefinition","category":"define","module":"std/types.ts","kind":"interface","text":"interface StdShapeDefinition {\n    role?: 'shape'\n    name: string\n    description?: string\n    category?: string\n    shape: Omit<SdfShapeShaderSpec, 'name' | 'description' | 'category'>\n}","summary":"A 2D shape: `shape:` gives the distance function and the shape's own props; fill, stroke, softness and bounds come for free.","description":"","source":{"file":"std/types.ts","line":215}},"define.StdGeneratorDefinition":{"id":"define.StdGeneratorDefinition","name":"StdGeneratorDefinition","category":"define","module":"std/types.ts","kind":"interface","text":"interface StdGeneratorDefinition<T extends ComponentProps> extends StdDefinitionBase<T> {\n    role?: 'generator'\n    /** A std composition or builder, or a `wgsl` body, returning the pixel's color. */\n    paint: ((params: GpuFragmentParams) => Expr) | WgslBody\n}","summary":"A generator: `paint:` produces a color from coordinates and needs nothing nested inside.","description":"`paint` is either a std composition (`rampOver(…)`, a builder function of the params) or a `wgsl` body.","source":{"file":"std/types.ts","line":229}},"define.StdCustomDefinition":{"id":"define.StdCustomDefinition","name":"StdCustomDefinition","category":"define","module":"std/types.ts","kind":"interface","text":"interface StdCustomDefinition<T extends ComponentProps> extends StdDefinitionBase<T> {\n    /** A label for the kind of thing this is. Nothing reads it. */\n    role?: 'simulation' | 'shapeEffect' | 'media' | 'structural' | 'filter' | 'generator' | 'overlay'\n    species?: 'custom'\n    requiresRTT?: boolean\n    requiresChild?: boolean\n    gpu: {\n        fragment: (params: GpuFragmentParams) => Expr\n        uvRemap?: GpuUvRemap\n    }\n}","summary":"A raw builder: `gpu:` holds a fragment function (and optionally a `uvRemap`) for effects the words cannot say yet.","description":"`role` here is only a label. Set `requiresChild` and `requiresRTT` yourself.","source":{"file":"std/types.ts","line":243}},"define.StdDefinition":{"id":"define.StdDefinition","name":"StdDefinition","category":"define","module":"std/types.ts","kind":"type","text":"type StdDefinition<T extends ComponentProps> =\n    | StdPointwiseFilterDefinition<T>\n    | StdGatherFilterDefinition<T>\n    | StdWgslFilterDefinition<T>\n    | StdWarpDefinition<T>\n    | StdShapeDefinition\n    | StdGeneratorDefinition<T>\n    | StdCustomDefinition<T>","summary":"What `defineShader` accepts: one of the definition shapes, told apart by which field carries the per-pixel work.","description":"`paint:` is a generator, `effect:` a filter (pointwise or gather from the effect), `map:` a distortion, `shape:` a 2D shape, `gpu:` a raw builder. `role` and `species` may be declared but must agree with the field; a contradiction throws when the definition is built.","source":{"file":"std/types.ts","line":263}},"values.PropRef":{"id":"values.PropRef","name":"PropRef","category":"values","module":"std/values.ts","kind":"interface","text":"interface PropRef {\n    readonly kind: 'prop'\n    readonly name: string\n}","summary":"A reference to one of the definition's props by name.","description":"Make one with `p`.","source":{"file":"std/values.ts","line":16}},"values.ScalarSource":{"id":"values.ScalarSource","name":"ScalarSource","category":"values","module":"std/values.ts","kind":"type","text":"type ScalarSource =\n    | PropRef\n    | {readonly kind: 'mul'; readonly a: ScalarSource; readonly b: ScalarSource}\n    | {readonly kind: 'radialMask'; readonly center: PropRef; readonly radius: PropRef; readonly falloff: PropRef}\n    | {readonly kind: 'signal'; readonly build: (params: SignalSlotParams) => import('../gpu/contract').Expr}","summary":"The shapes a `Scalar` can hold: a prop, a product of two values, a radial mask, or a live signal.","description":"","source":{"file":"std/values.ts","line":40}},"values.SignalSlotParams":{"id":"values.SignalSlotParams","name":"SignalSlotParams","category":"values","module":"std/values.ts","kind":"interface","text":"interface SignalSlotParams {\n    props: import('../gpu/contract').Expr\n    uniforms: Record<string, import('../gpu/contract').Expr>\n    ctx?: import('../gpu/contract').GpuFragmentParams['ctx']\n}","summary":"What a signal is given when it builds: the props, and the context values where the host has them (a warp's `map:` has no `ctx`).","description":"","source":{"file":"std/values.ts","line":47}},"values.ScalarInput":{"id":"values.ScalarInput","name":"ScalarInput","category":"values","module":"std/values.ts","kind":"type","text":"type ScalarInput = Scalar | PropRef","summary":"What a number-valued input accepts: a prop ref `p('x')` or a `Scalar`.","description":"","source":{"file":"std/values.ts","line":76}},"invoke.CtxToken":{"id":"invoke.CtxToken","name":"CtxToken","category":"invoke","module":"std/invoke.ts","kind":"interface","text":"interface CtxToken {\n    readonly kind: 'ctx'\n    readonly name: 'uv' | 'aspect' | 'time' | 'viewportSize' | 'logicalViewportSize' | 'pointer'\n}","summary":"One of the context values as a token you can pass where a word takes a value: `ctx.uv`, `ctx.time`, …","description":"","source":{"file":"std/invoke.ts","line":24}},"invoke.ArgSpec":{"id":"invoke.ArgSpec","name":"ArgSpec","category":"invoke","module":"std/invoke.ts","kind":"type","text":"type ArgSpec = PropRef | Scalar | CtxToken | number","summary":"Anything a word accepts as an input value: a prop ref `p('x')`, a `Scalar`, a context token, or a plain number.","description":"","source":{"file":"std/invoke.ts","line":62}},"invoke.SlotParams":{"id":"invoke.SlotParams","name":"SlotParams","category":"invoke","module":"std/invoke.ts","kind":"type","text":"type SlotParams = SignalSlotParams","summary":"The least a value needs to resolve against: the props, plus `ctx` where the host has one (a warp's `map:` does not).","description":"","source":{"file":"std/invoke.ts","line":65}},"lists.ListItemAt":{"id":"lists.ListItemAt","name":"ListItemAt","category":"lists","module":"std/lists.ts","kind":"interface","text":"interface ListItemAt {\n    /** A `position` field as `vec2f`, in the engine's stored form (y flipped: 1 − the authored y). */\n    position: (field: string) => Expr\n    /** A `color` field as `vec4f`, linear RGB with alpha. */\n    color: (field: string) => Expr\n    /** A `number` or `boolean` field as `f32` (true is 1, false is −1). */\n    number: (field: string) => Expr\n}","summary":"One item's fields at a loop index, read by field name and kind.","description":"","source":{"file":"std/lists.ts","line":26}},"lists.ListRef":{"id":"lists.ListRef","name":"ListRef","category":"lists","module":"std/lists.ts","kind":"interface","text":"interface ListRef {\n    /** How many items the list holds right now, as an `f32`. */\n    count: Expr\n    /** The fields of item `i`. */\n    at: (i: Expr) => ListItemAt\n}","summary":"A list prop bound for reading: the live item count and each item's fields.","description":"","source":{"file":"std/lists.ts","line":36}},"paint.Paint":{"id":"paint.Paint","name":"Paint","category":"paint","module":"std/paint/fields.ts","kind":"type","text":"type Paint = (params: GpuFragmentParams) => Expr","summary":"What a generator's `paint:` field takes: a function that returns the color at this pixel.","description":"","source":{"file":"std/paint/fields.ts","line":32}},"paint.Field":{"id":"paint.Field","name":"Field","category":"paint","module":"std/paint/fields.ts","kind":"type","text":"type Field = (params: GpuFragmentParams, coord: Expr) => Expr","summary":"A number per pixel, computed from a coordinate.","description":"Distance fields (`dist.*`) and `flowField` take a uv coordinate (0–1 across the canvas, y down). Noise fields take a 3D coordinate, so wrap them in `scaledVolume`.","source":{"file":"std/paint/fields.ts","line":40}},"paint.Palette":{"id":"paint.Palette","name":"Palette","category":"paint","module":"std/paint/fields.ts","kind":"type","text":"type Palette = (t: Expr, params: GpuFragmentParams) => Expr","summary":"A color for a field value between 0 and 1.","description":"Build one with `pair` (two colors) or `stops` (a multi-stop ramp), then hand it to `rampOver`.","source":{"file":"std/paint/fields.ts","line":47}},"paint.FieldScalar":{"id":"paint.FieldScalar","name":"FieldScalar","category":"paint","module":"std/paint/fields.ts","kind":"type","text":"type FieldScalar = ArgSpec | {base: ArgSpec; add?: number; mul?: number}","summary":"A number input: a prop `p('name')`, a plain number, or a prop remapped as `{base, add, mul}`.","description":"The remapped form resolves to `(base + add) * mul`. Use it when a slider's range is not the unit the word wants, such as a 0–100 slider that should become a small bias.","source":{"file":"std/paint/fields.ts","line":60}},"paint.NoiseBasis":{"id":"paint.NoiseBasis","name":"NoiseBasis","category":"paint","module":"std/paint/fields.ts","kind":"type","text":"type NoiseBasis = keyof typeof NOISE_BASES","summary":"The noise flavors `noiseField` can sample.","description":"`mx3` is smooth all-purpose noise, `perlin` classic gradient noise, `value` softer and blockier. All three return 0–1. `mx3signed` returns −1 to 1 for use with `threshold`.","source":{"file":"std/paint/fields.ts","line":493}},"gradients.Paint":{"id":"gradients.Paint","name":"Paint","category":"gradients","module":"std/paint/gradients.ts","kind":"type","text":"type Paint = (params: GpuFragmentParams) => Expr","summary":"What a generator's `paint:` field takes: a function that returns the color at this pixel.","description":"","source":{"file":"std/paint/gradients.ts","line":28}},"patterns.PatternPaint":{"id":"patterns.PatternPaint","name":"PatternPaint","category":"patterns","module":"std/paint/patterns.ts","kind":"type","text":"type PatternPaint = (params: GpuFragmentParams) => Expr","summary":"A pattern value: something you place in `paint:` or hand to another pattern word, yielding a color or a mask per pixel.","description":"","source":{"file":"std/paint/patterns.ts","line":36}},"patterns.PaintSlot":{"id":"patterns.PaintSlot","name":"PaintSlot","category":"patterns","module":"std/paint/patterns.ts","kind":"type","text":"type PaintSlot = PropRef | PatternPaint","summary":"What a pattern slot accepts: a prop `p('name')` or another pattern value.","description":"","source":{"file":"std/paint/patterns.ts","line":39}},"patterns.CellFrameConvention":{"id":"patterns.CellFrameConvention","name":"CellFrameConvention","category":"patterns","module":"std/paint/patterns.ts","kind":"type","text":"type CellFrameConvention = 'flippedY' | 'clockwise' | 'plain'","summary":"Which way a lattice's rows and rotation run.","description":"`'flippedY'` counts rows up from the bottom and rotates counter-clockwise (grids, triangles). `'clockwise'` counts rows down from the top and rotates clockwise (hexagons, truchet, cubes). `'plain'` counts down from the top and rotates counter-clockwise (weave). Use the one named in the line word's example; the others still work but shift the look.","source":{"file":"std/paint/patterns.ts","line":74}},"patterns.ScatterOptions":{"id":"patterns.ScatterOptions","name":"ScatterOptions","category":"patterns","module":"std/paint/patterns.ts","kind":"interface","text":"interface ScatterOptions {\n    cellSize: Expr | number\n    jitter: Expr | number\n    seed: Expr | number\n}","summary":"What `scatterPoint` and `scatterPoints` take.","description":"","source":{"file":"std/paint/patterns.ts","line":235}},"patterns.ScatteredPoint":{"id":"patterns.ScatteredPoint","name":"ScatteredPoint","category":"patterns","module":"std/paint/patterns.ts","kind":"interface","text":"interface ScatteredPoint {\n    point: Expr\n    offset: Expr\n    random: Expr\n}","summary":"One scattered point: where it sits, the pixel's offset from it, and three random 0–1 values for its cell.","description":"","source":{"file":"std/paint/patterns.ts","line":242}},"patterns.InkPlateSpec":{"id":"patterns.InkPlateSpec","name":"InkPlateSpec","category":"patterns","module":"std/paint/patterns.ts","kind":"interface","text":"interface InkPlateSpec {\n    readonly channel: 'cyan' | 'magenta' | 'yellow' | 'black'\n    readonly screenAngle: PropRef\n    readonly ink: PropRef\n}","summary":"One ink plate of a `cmykPress`: which channel it prints, its screen-angle prop (degrees) and its ink-color prop.","description":"","source":{"file":"std/paint/patterns.ts","line":891}},"patterns.GatherStage":{"id":"patterns.GatherStage","name":"GatherStage","category":"patterns","module":"std/paint/patterns.ts","kind":"type","text":"type GatherStage = (params: RttFilterParams) => Expr","summary":"A value computed per pixel from the layer inside a gather effect, such as a dither level.","description":"","source":{"file":"std/paint/patterns.ts","line":1010}},"patterns.PixelGrid":{"id":"patterns.PixelGrid","name":"PixelGrid","category":"patterns","module":"std/paint/patterns.ts","kind":"interface","text":"interface PixelGrid {\n    readonly size: PropRef\n    readonly coord: GatherStage\n    readonly source: GatherStage\n}","summary":"The pixel grid a dither works on, from `pixelGrid`: the cell `size` prop, the cell coordinate `coord`, and `source`, the child's color sampled once per cell.","description":"","source":{"file":"std/paint/patterns.ts","line":1016}},"figures.Paint":{"id":"figures.Paint","name":"Paint","category":"figures","module":"std/paint/figures.ts","kind":"type","text":"type Paint = (params: GpuFragmentParams) => Expr","summary":"What a generator's `paint:` field takes: a function that returns the color at this pixel.","description":"","source":{"file":"std/paint/figures.ts","line":26}},"figures.StrokedSegmentSlots":{"id":"figures.StrokedSegmentSlots","name":"StrokedSegmentSlots","category":"figures","module":"std/paint/figures.ts","kind":"interface","text":"interface StrokedSegmentSlots {\n    /** The two endpoints, position props made with `transformPosition`. */\n    from: PropRef\n    to: PropRef\n    /** Stroke thickness as a fraction of the canvas height (0.01 is a hairline). */\n    width: ArgSpec\n    /** Line style as a number: 0 solid, 1 dashed, 2 dotted. */\n    style: ArgSpec\n    /** Dash length (caps included) and the gap between dashes or dots, as fractions of the canvas height. */\n    dashLength: ArgSpec\n    gapLength: ArgSpec\n    /** Cap shape at each end as a number: 0 square, 1 rounded. */\n    capStart: ArgSpec\n    capEnd: ArgSpec\n    /** The stroke color prop. */\n    color: PropRef\n}","summary":"The inputs to `strokedSegment`.","description":"","source":{"file":"std/paint/figures.ts","line":46}},"figures.SineStrokeSlots":{"id":"figures.SineStrokeSlots","name":"SineStrokeSlots","category":"figures","module":"std/paint/figures.ts","kind":"interface","text":"interface SineStrokeSlots {\n    /** The wave's center, a position prop made with `transformPosition`, and its rotation in degrees. */\n    position: PropRef\n    angle: ArgSpec\n    /** Waves per canvas height along the line, and wave height as a fraction of the canvas height. */\n    frequency: ArgSpec\n    amplitude: ArgSpec\n    /** Band thickness (0–2) and edge softness (0–1). */\n    thickness: ArgSpec\n    softness: ArgSpec\n    /** The stroke color prop. */\n    color: PropRef\n}","summary":"The inputs to `sineStroke`.","description":"","source":{"file":"std/paint/figures.ts","line":100}},"compose.Layer":{"id":"compose.Layer","name":"Layer","category":"compose","module":"std/paint/compose.ts","kind":"type","text":"type Layer =\n    | Expr\n    | {\n        paint: Expr\n        /** `'add'` (default) sums light; `'screen'` folds it in with 1−(1−a)(1−b). */\n        blend?: 'add' | 'screen'\n        /** Scales the layer's contribution. */\n        opacity?: Expr | number\n        /** A 0–1 factor the layer is seen through (content behind a medium). */\n        behind?: Expr\n    }","summary":"One entry in `layers`: a color, or a color with options.","description":"`blend` is `'add'` (the default, sums light) or `'screen'` (brightens without blowing out). `opacity` scales the layer. `behind` is a 0–1 factor the layer is seen through, such as the transmittance of fog in front of it.","source":{"file":"std/paint/compose.ts","line":27}},"media.MediaSurfaceHalf":{"id":"media.MediaSurfaceHalf","name":"MediaSurfaceHalf","category":"media","module":"std/paint/media.ts","kind":"interface","text":"interface MediaSurfaceHalf {\n    gpu: {fragment: (params: GpuFragmentParams) => Expr}\n}","summary":"What a media word returns: the GPU half of a definition.","description":"Spread it into `defineShader({...})`.","source":{"file":"std/paint/media.ts","line":37}},"noise.Wave":{"id":"noise.Wave","name":"Wave","category":"noise","module":"std/paint/noise.ts","kind":"interface","text":"interface Wave {\n    x?: number\n    y?: number\n    t?: number\n    /** Radians, or `hashPhase(...)` for a seeded random phase. */\n    phase?: Expr | number\n}","summary":"One traveling sine wave, written as plain data.","description":"`x`, `y` and `t` are the wave's frequency along that axis: larger is tighter, and `t` is its drift speed along the layer's clock. Leave an axis out to ignore it. A negative value runs the wave the other way. `phase` shifts the wave, in radians.","source":{"file":"std/paint/noise.ts","line":41}},"noise.WaveTerm":{"id":"noise.WaveTerm","name":"WaveTerm","category":"noise","module":"std/paint/noise.ts","kind":"interface","text":"interface WaveTerm {\n    waves: Wave[]\n    weight?: number\n}","summary":"One term of a wave schedule: a single wave, or several multiplied together.","description":"Multiplying an x-wave by a y-wave gives an interference pattern instead of stripes. `weight` scales the term, default 1.","source":{"file":"std/paint/noise.ts","line":57}},"noise.Fold":{"id":"noise.Fold","name":"Fold","category":"noise","module":"std/paint/noise.ts","kind":"interface","text":"interface Fold {\n    offset: Expr\n    width: Expr\n    height: Expr\n    phase: Expr\n}","summary":"One ridge of a draped cloth as `foldSet` lays it out: where it rests across the drape, how wide and how high it is, and the phase that keeps its ripple out of step with its neighbours.","description":"","source":{"file":"std/paint/noise.ts","line":199}},"noise.DrapeOptions":{"id":"noise.DrapeOptions","name":"DrapeOptions","category":"noise","module":"std/paint/noise.ts","kind":"interface","text":"interface DrapeOptions {\n    time: Expr | number\n    /** A `wavyLine` wave list each spine wanders by (every fold starts at its own phase), and a gain on it. */\n    ripple: {waves: {freq: number; speed: number; amount: number}[]; gain?: Expr | number}\n    /** Moves all the folds together on the clock, like a breeze. */\n    sway?: {amount: Expr | number; rate?: number}\n    /** Each ridge rises and falls along its length so neighbours merge and part. */\n    swell?: {amount: number; freq: number; speed?: number}\n    /** 0–1: gathers the folds at the pinned edge. */\n    pinning?: Expr | number\n    /** How tightly a ridge falls away (default 2). */\n    sharpness?: number\n    /** 0–1 (default 0): flattens the crest into a plateau meeting the next fold in a tight valley. */\n    crease?: number\n}","summary":"The knobs that shape and animate a draped cloth, shared by `drapedFolds` and `clothCoords`.","description":"","source":{"file":"std/paint/noise.ts","line":321}},"noise.RgbStage":{"id":"noise.RgbStage","name":"RgbStage","category":"noise","module":"std/paint/noise.ts","kind":"type","text":"type RgbStage = (rgb: Expr, params: GpuFragmentParams) => Expr","summary":"A color-only step for `overRgb`: takes a paint's rgb and returns new rgb.","description":"Alpha is not touched.","source":{"file":"std/paint/noise.ts","line":937}},"noise.ReliefBasis":{"id":"noise.ReliefBasis","name":"ReliefBasis","category":"noise","module":"std/paint/noise.ts","kind":"interface","text":"interface ReliefBasis {\n    /** The height function: a 2D position in, a height out. */\n    readonly fn: unknown\n    /** Its name in the compiled shader. */\n    readonly hint: string\n}","summary":"A height field `noiseRelief` can emboss a layer with.","description":"Pick one from `reliefBases`.","source":{"file":"std/paint/noise.ts","line":1064}},"light.LightValue":{"id":"light.LightValue","name":"LightValue","category":"light","module":"std/paint/light.ts","kind":"type","text":"type LightValue = (params: GpuFragmentParams) => Expr","summary":"A light or a frame waiting for the shader's params.","description":"Call it with `params` inside a `paint` or `effect` function to get its value at this pixel.","source":{"file":"std/paint/light.ts","line":28}},"light.LightPart":{"id":"light.LightPart","name":"LightPart","category":"light","module":"std/paint/light.ts","kind":"type","text":"type LightPart = (frame: Expr, params: GpuFragmentParams) => Expr","summary":"A light shaped over a frame's coordinates.","description":"Parts for `radialFrame` read distance and angle (`vec2(dist, angle)`); parts for `beamFrame` read distance along and across the beam (`vec2(u, v)`). Evaluate one with `withFrame`, combine several with `modulate`.","source":{"file":"std/paint/light.ts","line":156}},"light.HeatRamp":{"id":"light.HeatRamp","name":"HeatRamp","category":"light","module":"std/paint/light.ts","kind":"type","text":"type HeatRamp = ReturnType<typeof heatRamp>","summary":"What `heatRamp` returns: call its `taps(heat, params)` to color a heat field.","description":"","source":{"file":"std/paint/light.ts","line":870}},"materials.SurfaceFrame":{"id":"materials.SurfaceFrame","name":"SurfaceFrame","category":"materials","module":"std/paint/materials.ts","kind":"interface","text":"interface SurfaceFrame {\n    /** Placement coordinates: the shape's own 0–1 square after center, scale and rotation. Absent with `stencil: 'none'`. */\n    sdfUV?: Expr\n    /** The field at this pixel. `.x` is the signed distance (negative inside), `.y/.z` pattern coordinates, `.w` depth. */\n    surf0?: Expr\n    /** The field one step right and one step down, when the stencil took them. */\n    surfX?: Expr\n    surfY?: Expr\n    /** The field one step left and one step up. Only with `stencil: {kind: 'central'}`. */\n    surfL?: Expr\n    surfD?: Expr\n    /** Sample the field at any placement coordinate, at full quality. */\n    sampler: (uv: Expr) => Expr\n    /** Sample the field at any placement coordinate, cheaply. Use it for neighbour taps. */\n    gradSampler: (uv: Expr) => Expr\n    /** Sample one texel without filtering, for discrete data packed into the field. */\n    texelSampler: (uv: Expr) => Expr\n    /** True when the shape is 3D or extruded. Known at build time, so you can branch in TypeScript. */\n    volumetric: boolean\n    /** `volumetric` as a 0/1 number, for use inside an expression. */\n    volFlag: Expr\n    /** True when `.y/.z` already hold the field's gradients instead of pattern coordinates. Skip your own slope taps. */\n    bakedGradients: boolean\n    /** The layer inside, as a texture. Present with `child: 'required'`. */\n    childTexture?: KitTexture\n}","summary":"What `shapedSurface` hands your `surface` function: the shape's distance field, sampled and ready to shade.","description":"Read the taps through `surfaceField` rather than directly. `volumetric` tells you at build time whether the shape is a 3D one (a sphere, a cube, an extruded SVG) or a flat one.","source":{"file":"std/paint/materials.ts","line":149}},"materials.ShapedSurfaceSpec":{"id":"materials.ShapedSurfaceSpec","name":"ShapedSurfaceSpec","category":"materials","module":"std/paint/materials.ts","kind":"interface","text":"interface ShapedSurfaceSpec {\n    /** Bake surface coordinates for 3D shapes so a pattern sticks to them (default 'none'). 'raw' follows the surface without seams, 'triplanar' projects from three sides. */\n    pattern?: 'none' | 'raw' | 'triplanar'\n    /** How thickness is measured through a 3D shape (default 'span'). 'firstLobe' measures the front wall only. */\n    chord?: 'span' | 'firstLobe'\n    /** Quality of the neighbour taps (default 'fast'). 'same' uses the full-quality sampler for them too. */\n    gradSampler?: 'fast' | 'same'\n    /** Let the field carry its own gradients in `.y/.z` (default 'none'). Check `frame.bakedGradients` before taking slopes. */\n    bakedGradients?: 'none' | 'volumetric' | 'all'\n    /** Which taps to take for you (default a right-and-down pair, `eps` 0.01 in placement units). 'none' takes none, 'centre' this pixel only, 'central' adds left and up. */\n    stencil?: 'none' | 'centre' | {kind: 'forward' | 'central'; eps: number}\n    /**\n     * Which sampler takes the centre tap (default 'quality'). 'fast' is cheaper on 3D shapes\n     * when the centre tap only gates coverage and the material takes its own quality tap\n     * where it needs one.\n     */\n    centreTap?: 'quality' | 'fast'\n    /** Displace the canvas uv before placement, to wobble or warp the whole shape. */\n    placeUV?: (params: GpuFragmentParams) => Expr\n    /** Require the layer inside and expose it as `frame.childTexture`. Without a child the effect draws nothing. */\n    child?: 'required'\n    /** Your material: shade the frame and return the color at this pixel. */\n    surface: (frame: SurfaceFrame, params: GpuFragmentParams) => Expr\n}","summary":"The options `shapedSurface` takes.","description":"Only `surface` is required.","source":{"file":"std/paint/materials.ts","line":180}},"materials.ShapedSurfaceEffect":{"id":"materials.ShapedSurfaceEffect","name":"ShapedSurfaceEffect","category":"materials","module":"std/paint/materials.ts","kind":"interface","text":"interface ShapedSurfaceEffect {\n    extraFields: Record<string, {schema: import('typegpu/data').AnyWgslData; initial: number | number[]}>\n    compute: GpuComputeNode\n    gpu: {fragment: (params: GpuFragmentParams) => Expr}\n}","summary":"The three fields `shapedSurface` returns.","description":"Spread them into your definition.","source":{"file":"std/paint/materials.ts","line":206}},"materials.SurfaceField":{"id":"materials.SurfaceField","name":"SurfaceField","category":"materials","module":"std/paint/materials.ts","kind":"interface","text":"interface SurfaceField {\n    /** The field at this pixel, and one step right and one step down when the stencil took them. */\n    s0: Expr\n    sX?: Expr\n    sY?: Expr\n    /** Signed distance to the shape's edge: negative inside, zero on the edge. */\n    sdf: Expr\n    /** One pixel, as a fraction of the canvas height. */\n    pxH: Expr\n    /** Canvas width divided by height. */\n    aspect: Expr\n}","summary":"The field values a material reads, bound once: the taps, the signed distance and the pixel size.","description":"","source":{"file":"std/paint/materials.ts","line":304}},"volume.InteriorRay":{"id":"volume.InteriorRay","name":"InteriorRay","category":"volume","module":"std/paint/volume.ts","kind":"interface","text":"interface InteriorRay {\n    /** The depth at which the ray enters the shape. 0 for flat shapes. */\n    entry: Expr\n    /** How far the ray travels inside the shape, roughly 0–1. */\n    length: Expr\n    /** The position inside the shape at depth `z` along the ray. */\n    at(z: Expr): {x: Expr; y: Expr}\n}","summary":"The line of sight through a shape's interior, from `interiorRay`.","description":"","source":{"file":"std/paint/volume.ts","line":22}},"volume.VolumeMarchStep":{"id":"volume.VolumeMarchStep","name":"VolumeMarchStep","category":"volume","module":"std/paint/volume.ts","kind":"interface","text":"interface VolumeMarchStep {\n    /** The step number, 0 first. A plain number, handy for naming per-step values. */\n    i: number\n    /** How far along the ray this step sits, 0–1. A plain number. */\n    t: number\n    /** The depth of this sample along the ray, to pass to `ray.at`. */\n    z: Expr\n}","summary":"One sample along a `volumeMarch`, handed to its callback.","description":"","source":{"file":"std/paint/volume.ts","line":166}},"radiance.GatherSpec":{"id":"radiance.GatherSpec","name":"GatherSpec","category":"radiance","module":"std/paint/radiance.ts","kind":"interface","text":"interface GatherSpec {\n    /** The pixel, as a 2D point in the shape field's coordinates. */\n    origin: Expr\n    /** The shape as a signed distance: given a point, how far it is from the edge (negative inside). */\n    field: (at: Expr) => Expr\n    /** How many directions to look in. 16 is nearly noise-free. Fixed when the shader compiles. */\n    cones: number\n    /** The most steps each direction may take toward the shape. */\n    steps: number\n    /** The distance beyond which light no longer counts, in field units. */\n    reach: Expr | number\n    /** Where each direction starts looking. The pixel's own distance to the shape is the natural start. */\n    start: Expr | number\n    /** How close counts as touching the edge, about one device pixel in field units. */\n    surfaceEps: Expr | number\n    /** A per-pixel 0–1 value that turns the fan of directions so its structure becomes fine grain. */\n    jitter: Expr | number\n    /** How bright the edge is where a direction touches it, as a single number. */\n    emission: (hit: {point: Expr; normal: Expr; distance: Expr; coverage: Expr}) => Expr\n    /** Values from outside that `emission` also uses. List them here or they are out of scope. */\n    deps?: Expr[]\n    hint?: string\n}","summary":"What `gatherIrradiance` needs.","description":"","source":{"file":"std/paint/radiance.ts","line":56}},"radiance.ShadowSpec":{"id":"radiance.ShadowSpec","name":"ShadowSpec","category":"radiance","module":"std/paint/radiance.ts","kind":"interface","text":"interface ShadowSpec {\n    /** The point being lit, moved a couple of pixels off the shape's edge along its normal. */\n    from: Expr\n    /** Where the light is, in the same coordinates. */\n    toward: Expr\n    /** The shape as a signed distance: given a point, how far it is from the edge (negative inside). */\n    field: (at: Expr) => Expr\n    /** The most steps the ray may take. 12 is typical. */\n    steps: number\n    /** How close counts as hitting the shape, about one device pixel in field units. */\n    surfaceEps: Expr | number\n    /** How wide the soft edge of the shadow is, 0–1. 0 is a hard shadow. */\n    softness?: Expr | number\n    hint?: string\n}","summary":"What `shadowVisibility` needs.","description":"","source":{"file":"std/paint/radiance.ts","line":178}},"radiance.IrradianceFieldSpec":{"id":"radiance.IrradianceFieldSpec","name":"IrradianceFieldSpec","category":"radiance","module":"std/paint/radiance.ts","kind":"interface","text":"interface IrradianceFieldSpec {\n    /**\n     * How many rays each texel casts per frame: `burst` on the first frame after something\n     * changes, `motion` while changes keep coming, then `refine` for each of `refineFrames`\n     * frames once the scene is still. After that nothing runs until the next change.\n     */\n    rays: {burst: number; motion: number; refine: number; refineFrames: number}\n    /** The most steps a ray may take toward the shape (default 32). */\n    steps?: number\n    /** The most steps a shadow ray may take. 0 turns shadows off entirely (default 12). */\n    shadowSteps?: number\n    /** The size of the square light texture, in texels per side. 512 is plenty; it is a smooth field. */\n    resolution: number\n    /**\n     * Your list prop of lights (made with `listPropConfig`) and the names of its item fields: a\n     * `position`, a `color` and a `number` for intensity. A light's position may be driven by the mouse.\n     */\n    lights: {prop: string; position: string; color: string; intensity: string}\n    /** The prop holding how far from the shape light is gathered, relative to the shape. */\n    reach: string\n    /** The prop holding how far light wraps around edges facing away from it, 0–1. */\n    wrap: string\n    /** The prop holding each light's range: the distance at which it falls to half strength. */\n    lightRange: string\n    /** The prop holding the shadow's soft-edge width, 0–1. */\n    shadowSoftness: string\n    /** The name the texture is published under for other compute passes (default 'irradianceTexture'). */\n    outputKey?: string\n    namePrefix?: string\n}","summary":"What `irradianceField` needs.","description":"Every string is the name of one of your props.","source":{"file":"std/paint/radiance.ts","line":328}},"voxels.VoxelStyle":{"id":"voxels.VoxelStyle","name":"VoxelStyle","category":"voxels","module":"std/paint/voxels.ts","kind":"type","text":"type VoxelStyle = voxelKit.VoxelStyle","summary":"What each block is: `'cube'`, `'sphere'` or `'rounded'` (a cube with rounded corners).","description":"","source":{"file":"std/paint/voxels.ts","line":42}},"voxels.VoxelGridSpace":{"id":"voxels.VoxelGridSpace","name":"VoxelGridSpace","category":"voxels","module":"std/paint/voxels.ts","kind":"type","text":"type VoxelGridSpace = voxelKit.VoxelGridSpace","summary":"Where the grid sits: `'shape'` turns with the shape like a built model, `'view'` stays fixed to the canvas.","description":"","source":{"file":"std/paint/voxels.ts","line":44}},"voxels.VoxelSurfaceSpec":{"id":"voxels.VoxelSurfaceSpec","name":"VoxelSurfaceSpec","category":"voxels","module":"std/paint/voxels.ts","kind":"interface","text":"interface VoxelSurfaceSpec {\n    /** The block style, `'cube' | 'sphere' | 'rounded'`. Mark the prop `compileTime: true`. */\n    style: PropRef\n    /** Where the grid sits, `'shape' | 'view'`. Mark the prop `compileTime: true`. */\n    gridSpace: PropRef\n    /** The edge length of one block, relative to the shape. 0.035 is about a tenth of the default sphere. */\n    voxelSize: PropRef\n    /** How eagerly cells along the surface fill in, −0.5 to 0.5. Negative carves the model thinner, positive puffs it out. */\n    fill: PropRef\n    /** The size of each block inside its cell, 0.3–1. 1 touches its neighbours, less opens gaps. */\n    voxelScale: PropRef\n    /** Corner rounding as a fraction of the block size, 0–1. A hairline chamfer on cubes, the corner radius of rounded cubes. */\n    bevel: PropRef\n    /** The soft-edge width of cast shadows, 0–1. 0 is razor sharp. */\n    shadowSoftness: PropRef\n    /** Optional camera tilt, in degrees. */\n    pitch?: PropRef\n    /** Optional camera turn, in degrees. */\n    yaw?: PropRef\n    /** Optional turntable speed. Declare `animatedTime: {speed: 'spin'}` too. A speed of 1 is one turn every ten seconds. */\n    spin?: PropRef\n    /** The direction the key light comes from, in degrees around the canvas. Shadows fall away from it. */\n    lightAngle: PropRef\n    /** How high the key light sits above the canvas, in degrees. Low for long shadows, high for flat top light. */\n    lightElevation: PropRef\n    /** The thickness given to flat shapes so they become a slab of blocks, relative to the shape. 3D shapes ignore it. */\n    depth: PropRef\n    /** Your material: the color of one block. Return `vec4(rgb, vox.coverage)`. */\n    surface: (vox: VoxelFrame, frame: SurfaceFrame, params: GpuFragmentParams) => Expr\n}","summary":"What `voxelSurface` needs.","description":"Every field but `surface` is a prop ref, `p('name')`.","source":{"file":"std/paint/voxels.ts","line":54}},"voxels.VoxelFrame":{"id":"voxels.VoxelFrame","name":"VoxelFrame","category":"voxels","module":"std/paint/voxels.ts","kind":"interface","text":"interface VoxelFrame {\n    /** The shape field basics: `sdf`, `pxH` (one pixel in uv) and `aspect`. */\n    field: SurfaceField\n    /** 1 when this is a real block, 0 for an empty slot. Return it as your alpha. */\n    coverage: Expr\n    /** The block's unit normal at this pixel. x right, y down, z negative toward the viewer. */\n    normal: Expr\n    /** How open the block's surroundings are, 1 in the open and lower in creases. Multiply ambient light by it. */\n    ao: Expr\n    /** How much shadow other blocks cast here, 0 in the light and 1 fully shadowed. */\n    shadow: Expr\n    /** The block's grid position as three whole numbers in a vec3. */\n    cell: Expr\n    /** A random 0–1 number that stays the same for this block every frame. Use it for per-block color variation. */\n    cellHash: Expr\n    /** How high the block sits, 0 at the bottom of the model and 1 at the top. */\n    heightT: Expr\n    /** How deep the block sits, 0 nearest the viewer and 1 farthest. */\n    depthT: Expr\n    /** How far this pixel is from the centre of its face toward the edge, 0–1. Seams live near 1. */\n    edge: Expr\n    /** The view ray into the scene, in the same convention as `normal`. */\n    view: Expr\n    /** The block style in force, known when the shader compiles. */\n    style: VoxelStyle\n}","summary":"One block, as `voxelSurface` hands it to your `surface`.","description":"","source":{"file":"std/paint/voxels.ts","line":92}},"frames.CentredFrame":{"id":"frames.CentredFrame","name":"CentredFrame","category":"frames","module":"std/frames.ts","kind":"interface","text":"interface CentredFrame {\n    /** The vector from the centre to the pixel, aspect-corrected (in canvas-height units). */\n    delta: Expr\n    /** The distance from the centre, in canvas-height units. */\n    dist: Expr\n}","summary":"What `centredFrame` gives you for each pixel.","description":"","source":{"file":"std/frames.ts","line":65}},"frames.SegmentFrame":{"id":"frames.SegmentFrame","name":"SegmentFrame","category":"frames","module":"std/frames.ts","kind":"interface","text":"interface SegmentFrame {\n    /** How far the pixel is along the line from `from` toward `to` (0 at `from`), in canvas-height units. */\n    along: Expr\n    /** The signed distance from the line: positive below a left-to-right line, negative above. */\n    across: Expr\n    /** The length of the line from `from` to `to`, in canvas-height units (never less than 0.0001). */\n    length: Expr\n}","summary":"What `segmentFrame` gives you for each pixel.","description":"","source":{"file":"std/frames.ts","line":102}},"frames.DirectionFrame":{"id":"frames.DirectionFrame","name":"DirectionFrame","category":"frames","module":"std/frames.ts","kind":"interface","text":"interface DirectionFrame {\n    /** The unit vector pointing along the direction. */\n    tangent: Expr\n    /** The unit vector at right angles to it. */\n    perp: Expr\n    /** Project a 2D point into the frame: its distance `along` the direction and `across` it. */\n    coordsOf(p: Expr): {along: Expr; across: Expr}\n}","summary":"What `directionFrame` gives you.","description":"","source":{"file":"std/frames.ts","line":172}},"signal.PointerSignal":{"id":"signal.PointerSignal","name":"PointerSignal","category":"signal","module":"std/signal.ts","kind":"interface","text":"interface PointerSignal {\n    readonly kind: 'pointer'\n    readonly teleportGuard: 'on' | 'off'\n}","summary":"Where the pointer is this frame, for a simulation step.","description":"`teleportGuard: 'on'` means a sudden jump (the cursor entering the canvas somewhere new) adds no motion that frame.","source":{"file":"std/signal.ts","line":21}},"signal.PointerSpeedSignal":{"id":"signal.PointerSpeedSignal","name":"PointerSpeedSignal","category":"signal","module":"std/signal.ts","kind":"interface","text":"interface PointerSpeedSignal {\n    readonly kind: 'pointerSpeed'\n    readonly max: number\n}","summary":"How fast the pointer is moving, for a simulation step.","description":"Measured in uv per second and capped at `max`, so a fast flick cannot overdrive the simulation.","source":{"file":"std/signal.ts","line":49}},"filter.TintTowardEffect":{"id":"filter.TintTowardEffect","name":"TintTowardEffect","category":"filter","module":"std/filter.ts","kind":"interface","text":"interface TintTowardEffect {\n    readonly kind: 'tintToward'\n    readonly color: PropRef\n    readonly amount: ScalarInput\n}","summary":"What `tintToward` returns: the color prop and the amount, ready for `effect:`.","description":"","source":{"file":"std/filter.ts","line":28}},"filter.DisplaceByEffect":{"id":"filter.DisplaceByEffect","name":"DisplaceByEffect","category":"filter","module":"std/filter.ts","kind":"interface","text":"interface DisplaceByEffect {\n    readonly kind: 'displaceBy'\n    readonly field: import('./sim').SimOutputRef\n    readonly strength: PropRef\n    readonly chromatic: PropRef\n    /** A prop marked `compileTime: true` holding `'stretch'`, `'transparent'`, `'mirror'` or `'wrap'`. */\n    readonly edges: PropRef\n}","summary":"What `displaceBy` returns: the simulation field and the props it reads, ready for `effect:`.","description":"","source":{"file":"std/filter.ts","line":55}},"effects.blurs.SampleStage":{"id":"effects.blurs.SampleStage","name":"SampleStage","category":"effects.blurs","module":"std/effects/blurs.ts","kind":"type","text":"type SampleStage = (params: RttFilterParams) => Expr","summary":"The first stage of a stacked effect: reads the child as a texture and returns the starting color at this pixel.","description":"Pass one to `gatherStack` as its first argument. The overlay stages then edit its result in order.","source":{"file":"std/effects/blurs.ts","line":42}},"effects.blurs.OverlayStage":{"id":"effects.blurs.OverlayStage","name":"OverlayStage","category":"effects.blurs","module":"std/effects/blurs.ts","kind":"type","text":"type OverlayStage = (color: Expr, params: RttFilterParams) => Expr","summary":"One editing stage of a stacked effect: takes the color so far and returns the edited color.","description":"Stages run in the order you list them in `gatherStack`.","source":{"file":"std/effects/blurs.ts","line":50}},"effects.blurs.GatherFrame":{"id":"effects.blurs.GatherFrame","name":"GatherFrame","category":"effects.blurs","module":"std/effects/blurs.ts","kind":"type","text":"type GatherFrame = (params: RttFilterParams) => Expr","summary":"A geometry that several stages of one effect share: a warped coordinate, a surface normal, a coverage mask.","description":"Make one with a frame word (`sphereFrame`, `glitchFrame`, `fluteFrame`, `peelFrame`), keep it in a module constant, and hand that same frame to every stage. It is computed once per pixel however many stages read it.","source":{"file":"std/effects/blurs.ts","line":90}},"effects.blurs.BlurPathSpec":{"id":"effects.blurs.BlurPathSpec","name":"BlurPathSpec","category":"effects.blurs","module":"std/effects/blurs.ts","kind":"interface","text":"interface BlurPathSpec {\n    readonly kind: motionBlurKit.MotionBlurPathKind\n    readonly focus: PropRef\n}","summary":"The path a motion blur smears along: its kind plus the prop that anchors it.","description":"Make one with `blurPath.linear`, `blurPath.orbit` or `blurPath.zoom` and pass it to `motionBlur`.","source":{"file":"std/effects/blurs.ts","line":337}},"effects.blurs.MotionBlurDetailSlots":{"id":"effects.blurs.MotionBlurDetailSlots","name":"MotionBlurDetailSlots","category":"effects.blurs","module":"std/effects/blurs.ts","kind":"interface","text":"interface MotionBlurDetailSlots {\n    /** −1 to 1: tint the taps along the streak with a spectrum, red at one end and violet at the other; the sign swaps the ends. */\n    dispersion?: PropRef\n    /** 0–1: concentrate the dispersion away from the centre; 0 spreads it evenly. */\n    falloff?: PropRef\n    /** 0–1: a clean zone around the centre that the blur leaves untouched. */\n    focus?: PropRef\n    /** −1 to 1: trail the streak off one side of each pixel instead of both. */\n    bias?: PropRef\n    /** 0–1: let bright pixels streak further than dark ones. */\n    highlights?: PropRef\n    /** 0–1: dither the tap comb with per-pixel noise so long streaks do not band. */\n    jitter?: PropRef\n    /** −1 to 1, zoom paths only: corkscrew the streak around the centre. */\n    spiral?: PropRef\n}","summary":"The optional lens and streak dials of `motionBlur`.","description":"Each is a prop ref whose value 0 is off; give every one `recompile: crosses(0)` so the shader switches between the plain gather and the detail gather exactly when a dial leaves or returns to 0.","source":{"file":"std/effects/blurs.ts","line":386}},"effects.blurs.ComputeBackedEffect":{"id":"effects.blurs.ComputeBackedEffect","name":"ComputeBackedEffect","category":"effects.blurs","module":"std/effects/blurs.ts","kind":"interface","text":"interface ComputeBackedEffect {\n    compute: GpuComputeNode\n    gpu: {fragment: (params: GpuFragmentParams) => Expr}\n}","summary":"The two halves a heavy blur adds to a definition: a pass that blurs the child ahead of time, and the fragment that reads it.","description":"Spread the whole object into a definition that declares `species: 'custom'`, `requiresRTT: true` and `requiresChild: true`.","source":{"file":"std/effects/blurs.ts","line":762}},"effects.blurs.VariableBlurDetailSlots":{"id":"effects.blurs.VariableBlurDetailSlots","name":"VariableBlurDetailSlots","category":"effects.blurs","module":"std/effects/blurs.ts","kind":"interface","text":"interface VariableBlurDetailSlots {\n    dispersion?: PropRef\n    jitter?: PropRef\n}","summary":"The optional lens dials of `progressiveBlur` and `tiltShift`.","description":"`dispersion` (−1 to 1) blurs red and blue by different radii for a defocus colour fringe that grows with the blur; `jitter` (0–1) dithers the tap comb so long radii do not band. Both are off at 0 and read per frame; give `dispersion` `recompile: crosses(0)`, since its kernel is only built while it is non-zero.","source":{"file":"std/effects/blurs.ts","line":864}},"effects.blurs.PeelShade":{"id":"effects.blurs.PeelShade","name":"PeelShade","category":"effects.blurs","module":"std/effects/blurs.ts","kind":"type","text":"type PeelShade = (params: RttFilterParams) => Expr","summary":"One shading term of a page peel: a number per pixel read against the peel frame.","description":"`curlShading`, `curlSheen`, `overhangShadow` and `revealShadow` each make one; `peelCompose` takes all four.","source":{"file":"std/effects/blurs.ts","line":1314}},"effects.stylize.GatherStage":{"id":"effects.stylize.GatherStage","name":"GatherStage","category":"effects.stylize","module":"std/effects/stylize.ts","kind":"type","text":"type GatherStage = (params: RttFilterParams) => Expr","summary":"A stage that reads the layer inside (rendered to a texture) and gives one value per pixel.","description":"","source":{"file":"std/effects/stylize.ts","line":30}},"effects.stylize.KuwaharaBrush":{"id":"effects.stylize.KuwaharaBrush","name":"KuwaharaBrush","category":"effects.stylize","module":"std/effects/stylize.ts","kind":"type","text":"type KuwaharaBrush = (params: RttFilterParams) => {quadrants: {sum: Expr; sq: Expr}[]; invN: number}","summary":"The brush `kuwahara` builds: four overlapping patches of samples around each pixel.","description":"Pass it to `wash`.","source":{"file":"std/effects/stylize.ts","line":77}},"effects.stylize.SobelRing":{"id":"effects.stylize.SobelRing","name":"SobelRing","category":"effects.stylize","module":"std/effects/stylize.ts","kind":"type","text":"type SobelRing = (params: RttFilterParams) => {sobelA: Expr; sobelB: Expr; centre: Expr; centreLum: Expr}","summary":"The ring of edge samples `sobelTaps` builds around each pixel.","description":"Pass it to `chalkSketch`.","source":{"file":"std/effects/stylize.ts","line":203}},"effects.stylize.FragmentStage":{"id":"effects.stylize.FragmentStage","name":"FragmentStage","category":"effects.stylize","module":"std/effects/stylize.ts","kind":"type","text":"type FragmentStage = (params: GpuFragmentParams) => Expr","summary":"A stage of the ASCII look: one value per pixel, computed in the fragment.","description":"","source":{"file":"std/effects/stylize.ts","line":286}},"effects.lens.SpectralLensSlots":{"id":"effects.lens.SpectralLensSlots","name":"SpectralLensSlots","category":"effects.lens","module":"std/effects/lens.ts","kind":"interface","text":"interface SpectralLensSlots {\n    /** Position prop (`transform: transformPosition`) the split, focus, warp and swirl pivot around. */\n    center: PropRef\n    /** 0–1, how far apart the color copies are pushed; 0 is off. */\n    spread: PropRef\n    /** −1 to 1, slides the copies toward one end of the spread; 0 spaces them evenly. */\n    bias: PropRef\n    /** Direction of the spread in degrees (`transform: transformAngle`). */\n    angle: PropRef\n    /** 0–1, bends the spread from a straight line (0) to a burst out from the center (1). */\n    perspective: PropRef\n    /** Number of color copies, 2–50; more is smoother and costlier. */\n    count: PropRef\n    /** 0–1, how strongly each copy takes its own color from the spectrum; 0 keeps the original color. */\n    dispersion: PropRef\n    /** −1 to 1, where the dispersion lands: −1 only near the center, 1 only toward the edges. */\n    dispersionShift: PropRef\n    /** 0–1, turns the dispersion colors around the hue wheel; 1 is a full turn. */\n    dispersionColor: PropRef\n    /** 0–1, fades the split out in a round zone at the center. */\n    focusCenter: PropRef\n    /** 0–1, fades the split out toward the edges; 1 restores the original image there. */\n    focusEdges: PropRef\n    /** −1 to 1, rotates the copies around the center by an angle that grows along the spread. */\n    swirl: PropRef\n    /** 0–1, scatters the spread direction with noise; 0 is off. */\n    noise: PropRef\n    /** 0–1, how fine the direction noise is. */\n    noiseFrequency: PropRef\n    /** 0–1, shifts the noise pattern (a seed). */\n    noiseOffset: PropRef\n    /** −1 to 1, fisheye bulge (positive) or pincushion pinch (negative). */\n    lensBulge: PropRef\n    /** 0–1, squeezes the image toward a circular outline. */\n    lensCircle: PropRef\n    /** 0–1, breaks up the copy edges with grain. */\n    grainMixer: PropRef\n    /** 0–1, black-and-white grain laid over the result. */\n    grainOverlay: PropRef\n}","summary":"The props `spectralLens` reads.","description":"Every field is a prop ref (`p('name')`); ranges are the useful slider ranges.","source":{"file":"std/effects/lens.ts","line":80}},"effects.reveal.AngularDirection":{"id":"effects.reveal.AngularDirection","name":"AngularDirection","category":"effects.reveal","module":"std/effects/reveal.ts","kind":"type","text":"type AngularDirection = 'cw' | 'ccw' | 'both'","summary":"Which way an angular sweep turns.","description":"Baked into the shader, so a change rebuilds it.","source":{"file":"std/effects/reveal.ts","line":134}},"effects.reveal.RevealSlots":{"id":"effects.reveal.RevealSlots","name":"RevealSlots","category":"effects.reveal","module":"std/effects/reveal.ts","kind":"interface","text":"interface RevealSlots {\n    /** The order pixels disappear in, from a `coverage` word. */\n    coverage: Coverage\n    /** How far the wipe has gone, 0–1 (0 fully visible, 1 fully gone). */\n    progress: ArgSpec\n    /** Width of the soft edge, 0–1 (0 is a hard edge). */\n    feather: ArgSpec\n    /** A boolean prop (`transform: transformBoolean`) that reverses the order. */\n    invert: ArgSpec\n}","summary":"What `reveal` needs: the order pixels go in, and the three timing props every wipe shares.","description":"","source":{"file":"std/effects/reveal.ts","line":248}},"effects.instances.GridSlots":{"id":"effects.instances.GridSlots","name":"GridSlots","category":"effects.instances","module":"std/effects/instances.ts","kind":"interface","text":"interface GridSlots {\n    columns: PropRef\n    rows: PropRef\n    gapX: PropRef\n    gapY: PropRef\n    stagger: PropRef\n    /** A compile-time select: `'none'`, `'alternate-flip-x'`, `'alternate-flip-y'` or `'both'`. */\n    flip: PropRef\n}","summary":"The props of a grid layout: `columns` and `rows` (counts), `gapX` and `gapY` (fractions of the canvas width and height), `stagger` (0 to 1, shifts every other row by that much of a cell) and `flip`, a compile-time select that mirrors alternate cells (`'none'`, `'alternate-flip-x'`, `'alternate-flip-y'`, `'both'`).","description":"","source":{"file":"std/effects/instances.ts","line":31}},"effects.instances.RadialSlots":{"id":"effects.instances.RadialSlots","name":"RadialSlots","category":"effects.instances","module":"std/effects/instances.ts","kind":"interface","text":"interface RadialSlots {\n    count: PropRef\n    radius: PropRef\n    startAngle: PropRef\n    sweep: PropRef\n    faceCenter: PropRef\n}","summary":"The props of a radial layout: `count`, `radius` (a fraction of the canvas height), `startAngle` and `sweep` (degrees), and `faceCenter`, a boolean that turns each copy to face the middle of the circle.","description":"","source":{"file":"std/effects/instances.ts","line":45}},"effects.instances.LineSlots":{"id":"effects.instances.LineSlots","name":"LineSlots","category":"effects.instances","module":"std/effects/instances.ts","kind":"interface","text":"interface LineSlots {\n    count: PropRef\n    direction: PropRef\n    spacing: PropRef\n}","summary":"The props of a line layout: `count`, `direction` (degrees, 0 points right) and `spacing` between copies (a fraction of the canvas height).","description":"","source":{"file":"std/effects/instances.ts","line":56}},"effects.instances.PlacementSpec":{"id":"effects.instances.PlacementSpec","name":"PlacementSpec","category":"effects.instances","module":"std/effects/instances.ts","kind":"type","text":"type PlacementSpec =\n    | {kind: 'grid'; slots: GridSlots}\n    | {kind: 'radial'; slots: RadialSlots}\n    | {kind: 'line'; slots: LineSlots}","summary":"One layout, built by `grid`, `radial` or `line`.","description":"","source":{"file":"std/effects/instances.ts","line":63}},"effects.instances.PlacementPick":{"id":"effects.instances.PlacementPick","name":"PlacementPick","category":"effects.instances","module":"std/effects/instances.ts","kind":"interface","text":"interface PlacementPick {\n    mode: PropRef\n    members: {grid: PlacementSpec; radial: PlacementSpec; linear: PlacementSpec}\n}","summary":"A layout chosen by a select prop, built by `byMode`.","description":"","source":{"file":"std/effects/instances.ts","line":106}},"effects.instances.VariationSlots":{"id":"effects.instances.VariationSlots","name":"VariationSlots","category":"effects.instances","module":"std/effects/instances.ts","kind":"interface","text":"interface VariationSlots {\n    scale: PropRef\n    rotation: PropRef\n    opacity: PropRef\n    jitter: {position: PropRef; rotation: PropRef; scale: PropRef; opacity: PropRef}\n    seed: PropRef\n    /** Layout animation phase, 0 to 1; 1 is the same layout as 0. */\n    phase: PropRef\n}","summary":"How each copy differs from the last.","description":"`scale` and `opacity` multiply with every copy (1 keeps them equal), `rotation` adds per copy in radians (give the prop a degrees-to-radians transform). The `jitter` props (0 to 1) add seeded randomness to position, rotation, scale and opacity; `seed` re-rolls it. `phase` (0 to 1) slides the whole layout by one cell or one copy and wraps seamlessly, for looping animation.","source":{"file":"std/effects/instances.ts","line":135}},"effects.instances.RepeatInstancesRecipe":{"id":"effects.instances.RepeatInstancesRecipe","name":"RepeatInstancesRecipe","category":"effects.instances","module":"std/effects/instances.ts","kind":"interface","text":"interface RepeatInstancesRecipe {\n    /** Crop insets on the copied area, each a fraction (0 to 0.49) of its width or height. */\n    source: {crop: {left: PropRef; right: PropRef; top: PropRef; bottom: PropRef}}\n    placement: PlacementPick | PlacementSpec\n    variation: VariationSlots\n    /** Hue rotation added per copy, in radians. Off at 0; give the prop `recompile: crosses(0)`. */\n    hueShift?: PropRef\n    /** A compile-time select: `'forward'` stacks later copies on top, `'backward'` underneath. */\n    order: PropRef\n}","summary":"Everything `repeatInstances` needs: what to copy, where to put the copies, and how they vary.","description":"","source":{"file":"std/effects/instances.ts","line":146}},"effects.overlay.DetectionOverlaySlots":{"id":"effects.overlay.DetectionOverlaySlots","name":"DetectionOverlaySlots","category":"effects.overlay","module":"std/effects/overlay.ts","kind":"interface","text":"interface DetectionOverlaySlots {\n    /** Select: what counts as an object, `'alpha' | 'bright' | 'dark' | 'red' | 'green' | 'blue'`. */\n    detectionMode: PropRef\n    /** 0–1, how strong the content must be to count. */\n    threshold: PropRef\n    /** Select: how the canvas is cut into cells, `'grid' | 'quadtree' | 'mosaic'`. */\n    layout: PropRef\n    /** Base cell size in pixels (24–800). */\n    cellSize: PropRef\n    /** Select (integer 1–5): how many times a quadtree or mosaic cell may split in four. Ignored by `'grid'`. */\n    maxDepth: PropRef\n    /** Select: `'full'` box outline or `'corners'` brackets. */\n    boxStyle: PropRef\n    /** Outline thickness in pixels. */\n    lineWidth: PropRef\n    /** Outline corner radius in pixels (full boxes only). */\n    cornerRadius: PropRef\n    /** Box outline color. */\n    strokeColor: PropRef\n    /** Box fill color; use a translucent one. */\n    fillColor: PropRef\n    /** Label text color. */\n    labelColor: PropRef\n    /** Label pill color. */\n    labelBackgroundColor: PropRef\n    /** Select: what the label says, `'none' | 'dimensions' | 'percentage'`. */\n    labelMode: PropRef\n    /** Select: the box corner the label sits at, `'bottom-left' | 'bottom-right' | 'top-left' | 'top-right'`. */\n    labelPosition: PropRef\n    /** Boolean prop (`transform: transformBoolean`): label inside the box (on) or outside (off). */\n    labelInset: PropRef\n    /** Label pill corner radius in pixels. */\n    labelRadius: PropRef\n    /** Google Fonts family for the label (a `font-family` prop). */\n    fontFamily: PropRef\n    /** Font weight for the label (a `font-weight` prop). */\n    fontWeight: PropRef\n    /** Label text size as a fraction of canvas height (0.01–0.5). */\n    fontSize: PropRef\n    /** Label letter spacing in em. */\n    letterSpacing: PropRef\n}","summary":"The props `detectionOverlay` reads.","description":"Every field is a prop ref (`p('name')`). Props marked \"select\" are declared `compileTime: true`.","source":{"file":"std/effects/overlay.ts","line":32}},"effects.overlay.MotionTrackerHudSlots":{"id":"effects.overlay.MotionTrackerHudSlots","name":"MotionTrackerHudSlots","category":"effects.overlay","module":"std/effects/overlay.ts","kind":"interface","text":"interface MotionTrackerHudSlots {\n    /** How many trackers to draw, 1–64. Must be the same prop the tracker simulation reads. */\n    trackers: PropRef\n    /** 0–1, how visible the keyframe trail behind each tracker is. */\n    trail: PropRef\n    /** Gizmo size in pixels (8–80). */\n    markerSize: PropRef\n    /** Stroke width in pixels. */\n    lineWidth: PropRef\n    /** Color of the tracker gizmo (brackets, crosshair, dot). */\n    markerColor: PropRef\n    /** Color of the keyframe diamonds along the trail. */\n    keyframeColor: PropRef\n    /** Color of the motion path between keyframes. */\n    pathColor: PropRef\n}","summary":"The props `motionTrackerHud` reads.","description":"Every field is a prop ref (`p('name')`).","source":{"file":"std/effects/overlay.ts","line":190}},"simulate.WaveOp":{"id":"simulate.WaveOp","name":"WaveOp","category":"simulate","module":"std/sim.ts","kind":"interface","text":"interface WaveOp {\n    readonly kind: 'op.wave'\n    readonly damping: PropRef\n}","summary":"A step that makes the grid ripple like a water surface, fading by `damping`.","description":"Needs `history: 2` on the grid, because a wave reads the two previous frames. `damping` is a prop on a 0–20 scale: 0 rings forever, 20 dies in a few frames.","source":{"file":"std/sim.ts","line":35}},"simulate.SplatOp":{"id":"simulate.SplatOp","name":"SplatOp","category":"simulate","module":"std/sim.ts","kind":"interface","text":"interface SplatOp {\n    readonly kind: 'op.splat'\n    readonly at: PointerSignal\n    readonly amount: PointerSpeedSignal\n    readonly radius: PropRef\n}","summary":"A step that presses a soft round brush into the grid wherever the pointer moves.","description":"`at` is the pointer signal, `amount` the pointer-speed signal that scales the push (a still pointer adds nothing), `radius` a prop on a 0.1–1 scale.","source":{"file":"std/sim.ts","line":49}},"simulate.GradientOp":{"id":"simulate.GradientOp","name":"GradientOp","category":"simulate","module":"std/sim.ts","kind":"interface","text":"interface GradientOp {\n    readonly kind: 'op.gradient'\n}","summary":"A derived output: the slope of the grid at every cell, as a 2D vector.","description":"This is what a distortion wants: `displaceBy` moves each pixel along the slope.","source":{"file":"std/sim.ts","line":62}},"simulate.GridStepOp":{"id":"simulate.GridStepOp","name":"GridStepOp","category":"simulate","module":"std/sim.ts","kind":"type","text":"type GridStepOp = WaveOp | SplatOp","summary":"Any step that advances the grid each frame.","description":"","source":{"file":"std/sim.ts","line":67}},"simulate.GridDeriveOp":{"id":"simulate.GridDeriveOp","name":"GridDeriveOp","category":"simulate","module":"std/sim.ts","kind":"type","text":"type GridDeriveOp = GradientOp","summary":"Any output derived from the grid after the steps have run.","description":"","source":{"file":"std/sim.ts","line":69}},"simulate.GridSimConfig":{"id":"simulate.GridSimConfig","name":"GridSimConfig","category":"simulate","module":"std/sim.ts","kind":"interface","text":"interface GridSimConfig {\n    /** Cells per side of the square grid. 128 is plenty for a ripple field. */\n    resolution: number\n    /** How many past frames the steps read. A wave needs 2. */\n    history: number\n    /** The steps that advance the grid, run in this order once per frame. */\n    step: GridStepOp[]\n    /** Named outputs an effect can read through `sim.output(name)`. */\n    derive: Record<string, GridDeriveOp>\n    /** Let the engine stop running the simulation once the field has faded to nothing. */\n    // 'derived-from-damping': the settle window is computed from the damping value each frame\n    // (see `createWaveFieldSim`); low damping keeps the sim running, high damping sleeps fast.\n    rest?: {settlesWhen: 'derived-from-damping'}\n}","summary":"What a grid simulation declares: its size, how many past frames it keeps, its steps, and its named outputs.","description":"","source":{"file":"std/sim.ts","line":134}},"simulate.SimOutputRef":{"id":"simulate.SimOutputRef","name":"SimOutputRef","category":"simulate","module":"std/sim.ts","kind":"interface","text":"interface SimOutputRef {\n    readonly kind: 'simOutput'\n    readonly sim: GridSim\n    readonly output: string\n}","summary":"A handle to one named output of a simulation, the thing you hand to `displaceBy`.","description":"","source":{"file":"std/sim.ts","line":154}},"sim.grids.GridRoot":{"id":"sim.grids.GridRoot","name":"GridRoot","category":"sim.grids","module":"std/sim/grids.ts","kind":"type","text":"type GridRoot = NonNullable<GpuFragmentParams['gpu']>['root']","summary":"The GPU device handle the build callback receives, for creating the buffers and passes your stages use.","description":"","source":{"file":"std/sim/grids.ts","line":51}},"sim.grids.GridFrameParams":{"id":"sim.grids.GridFrameParams","name":"GridFrameParams","category":"sim.grids","module":"std/sim/grids.ts","kind":"interface","text":"interface GridFrameParams {\n    pointer?: {x: number; y: number}\n    deltaTime?: number\n    dimensions?: {width: number; height: number}\n}","summary":"What the renderer reports each frame: the pointer in uv, the frame delta in seconds, the canvas size in pixels.","description":"","source":{"file":"std/sim/grids.ts","line":54}},"sim.grids.GridFrame":{"id":"sim.grids.GridFrame","name":"GridFrame","category":"sim.grids","module":"std/sim/grids.ts","kind":"interface","text":"interface GridFrame {\n    /** Seconds since the last frame, never more than `clampDt`. */\n    dt: number\n    /** Wall-clock milliseconds this frame. */\n    // `Date.now()` — the settle gates' clock.\n    now: number\n    /** A numeric prop's current value, or `fallback` when it is not a number this frame. */\n    num(key: string, fallback: number): number\n    /** Any prop's current value. */\n    getCpuValue(key: string): unknown\n    frameParams: GridFrameParams\n}","summary":"One frame, as every stage sees it: the clamped delta, the clock, and your props.","description":"","source":{"file":"std/sim/grids.ts","line":61}},"sim.grids.GridStage":{"id":"sim.grids.GridStage","name":"GridStage","category":"sim.grids","module":"std/sim/grids.ts","kind":"interface","text":"interface GridStage {\n    /** Return false to run nothing this frame (an input the stage needs has not arrived yet). */\n    // Frame gate: false → the whole frame returns null (a late-bound input is still pending).\n    ready?(): boolean\n    /** Bookkeeping before any gate: pointer smoothing, activity flags. */\n    tick?(f: GridFrame): void\n    /** Return true to skip the whole frame. The last published texture stays on screen. */\n    skip?(f: GridFrame): boolean\n    /** Add this stage's GPU passes and value writes to the frame. */\n    emit?(f: GridFrame, nodes: ComputeStep[]): void\n}","summary":"One stage of the frame.","description":"Build them with `op.*` rather than by hand.\n\nEach frame runs every stage's `ready`, then every `tick`, then every `skip`, then every `emit`, in list order.","source":{"file":"std/sim/grids.ts","line":81}},"sim.grids.GridSimConfig":{"id":"sim.grids.GridSimConfig","name":"GridSimConfig","category":"sim.grids","module":"std/sim/grids.ts","kind":"interface","text":"interface GridSimConfig {\n    /** The textures the fragment samples, by the name it reads them under in `computeOutputs`. */\n    // State/display textures, child RTT handles.\n    outputs: Record<string, unknown>\n    /** Receive the child's picture once it exists (it is allocated after the layer composes). */\n    // Late input binding (child RTT → luma prepass, child-modulated kernels). Spread only if set.\n    bindInputs?: (resolve: (key: string) => {texture: unknown} | undefined) => void\n    /** Longest frame step accepted, in seconds. 0.016–0.05 keeps a tab switch from jumping the field. */\n    clampDt: number\n    /** What to do on a frame with no elapsed time: run anyway (default) or skip it. */\n    // Liquify's guard.\n    zeroDt?: 'run' | 'skip'\n    /** The frame program, in order. */\n    stages: GridStage[]\n}","summary":"What a GPU grid simulation declares: its outputs, its frame-delta clamp, and its stages.","description":"","source":{"file":"std/sim/grids.ts","line":94}},"sim.grids.SortPassStage":{"id":"sim.grids.SortPassStage","name":"SortPassStage","category":"sim.grids","module":"std/sim/grids.ts","kind":"interface","text":"interface SortPassStage extends GridStage {\n    side(): 'A' | 'B'\n}","summary":"A sort chain's stage, which also reports which of its two buffers holds the current result.","description":"","source":{"file":"std/sim/grids.ts","line":166}},"sim.grids.HostFrame":{"id":"sim.grids.HostFrame","name":"HostFrame","category":"sim.grids","module":"std/sim/grids.ts","kind":"interface","text":"interface HostFrame {\n    /** Seconds since the last frame, never more than `clampDt`. */\n    // Wall-clock delta (these sims ignore the renderer's deltaTime).\n    dt: number\n    now: number\n    pointer: {x: number; y: number}\n    frameParams: unknown\n}","summary":"One frame of a JavaScript-stepped grid: the clamped delta, the clock and the pointer in uv.","description":"","source":{"file":"std/sim/grids.ts","line":421}},"sim.grids.HostStep":{"id":"sim.grids.HostStep","name":"HostStep","category":"sim.grids","module":"std/sim/grids.ts","kind":"interface","text":"interface HostStep {\n    name: string\n    run(f: HostFrame): void | 'skip'\n}","summary":"One named JavaScript step.","description":"Return `'skip'` to end the frame early.","source":{"file":"std/sim/grids.ts","line":431}},"sim.fluids.FluidRoot":{"id":"sim.fluids.FluidRoot","name":"FluidRoot","category":"sim.fluids","module":"std/sim/fluids.ts","kind":"type","text":"type FluidRoot = NonNullable<GpuFragmentParams['gpu']>['root']","summary":"The GPU device handle the build callback receives, for creating the resources your passes use.","description":"","source":{"file":"std/sim/fluids.ts","line":56}},"sim.fluids.FluidKernel":{"id":"sim.fluids.FluidKernel","name":"FluidKernel","category":"sim.fluids","module":"std/sim/fluids.ts","kind":"type","text":"type FluidKernel = (cx: number, cy: number) => void","summary":"A pass function run once per grid cell, with the cell's column and row.","description":"","source":{"file":"std/sim/fluids.ts","line":60}},"sim.fluids.FluidFrameParams":{"id":"sim.fluids.FluidFrameParams","name":"FluidFrameParams","category":"sim.fluids","module":"std/sim/fluids.ts","kind":"interface","text":"interface FluidFrameParams {\n    pointer?: PointerLike\n    deltaTime?: number\n    dimensions?: {width: number; height: number}\n}","summary":"What the renderer reports each frame: the pointer in uv, the frame delta in seconds, the canvas size in pixels.","description":"","source":{"file":"std/sim/fluids.ts","line":63}},"sim.fluids.FluidFrame":{"id":"sim.fluids.FluidFrame","name":"FluidFrame","category":"sim.fluids","module":"std/sim/fluids.ts","kind":"interface","text":"interface FluidFrame {\n    /** Seconds since the last frame, never more than `maxDeltaTime` (0.033 by default). */\n    dt: number\n    /** Wall-clock milliseconds this frame. */\n    // `Date.now()` (wall-clock bookkeeping; the idle gates run on simulated `dt`).\n    now: number\n    /** The simulation's own clock: the sum of every `dt` so far. */\n    // An init part may reset it (Fog's warm-up).\n    readonly elapsed: number\n    setElapsed(t: number): void\n    /** The pointer this frame, when the config declared `pointer`. Null otherwise. */\n    // The noun-owned tracker's sample. Ribbons own their own.\n    ptr: PointerFrame | null\n    /** A numeric prop's current value, or `fallback` when it is not a number this frame. */\n    num(key: string, fallback: number): number\n    /** Any prop's current value. */\n    getCpuValue(key: string): unknown\n    frameParams: FluidFrameParams\n    /** Write the per-frame values directly, for a part that rewrites them between passes. */\n    // Per-stamp rewrites inside ribbon thunks.\n    writeParams(v: unknown): void\n}","summary":"One frame, as every part and `values` sees it: the clamped delta, the clocks, the pointer and your props.","description":"","source":{"file":"std/sim/fluids.ts","line":70}},"sim.fluids.FluidSetupCtx":{"id":"sim.fluids.FluidSetupCtx","name":"FluidSetupCtx","category":"sim.fluids","module":"std/sim/fluids.ts","kind":"interface","text":"interface FluidSetupCtx {\n    params: GpuFragmentParams\n    root: FluidRoot\n    /** Cells per side of the grid. */\n    n: number\n    buffers: {\n        velA: unknown; velB: unknown; dyeA: unknown; dyeB: unknown\n        pressure: unknown; divergence: unknown; maskBuf?: unknown\n    }\n    /** The buffer holding the per-frame values, for a part with its own resources. */\n    paramsBuffer: unknown\n    /** The resource group every solver pass runs on. */\n    fluidBindGroup: unknown\n    /** Turn a pass function into a runnable pass, on the fluid's resources unless another group is given. */\n    pass(kernel: FluidKernel, bindGroup?: unknown): KitComputePipeline\n}","summary":"What a part receives when it builds its passes: the device, the grid size, the buffers and a pass builder.","description":"","source":{"file":"std/sim/fluids.ts","line":94}},"sim.fluids.FluidEmitter":{"id":"sim.fluids.FluidEmitter","name":"FluidEmitter","category":"sim.fluids","module":"std/sim/fluids.ts","kind":"interface","text":"interface FluidEmitter<TParams = unknown> {\n    setup?(ctx: FluidSetupCtx): void\n    /** Bookkeeping before the idle gate: sample the pointer, drift a color cycle, mark activity. */\n    tick?(f: FluidFrame): void\n    /** Return true to skip the whole frame. The settled field stays on screen. */\n    skip?(f: FluidFrame): boolean\n    /** Add this frame's injection passes. They run before the solve. */\n    emit?(f: FluidFrame, nodes: ComputeStep[], values: TParams): void\n}","summary":"Something that injects into the field each frame.","description":"Build one with `splat` or `cursorRibbon`.","source":{"file":"std/sim/fluids.ts","line":112}},"sim.fluids.FluidSolveStage":{"id":"sim.fluids.FluidSolveStage","name":"FluidSolveStage","category":"sim.fluids","module":"std/sim/fluids.ts","kind":"interface","text":"interface FluidSolveStage {\n    stage: 'pre' | 'post'\n    kernel: FluidKernel\n}","summary":"A pass that runs inside every solve, before it (`pre`) or after it (`post`).","description":"Build one with `ambientForce` or `restoreToward`.","source":{"file":"std/sim/fluids.ts","line":124}},"sim.fluids.FluidInitIo":{"id":"sim.fluids.FluidInitIo","name":"FluidInitIo","category":"sim.fluids","module":"std/sim/fluids.ts","kind":"interface","text":"interface FluidInitIo<TParams> {\n    nodes: ComputeStep[]\n    writeParams(v: TParams): ComputeStep\n    solve(nodes: ComputeStep[], jacobiIters: number): void\n}","summary":"What an init part appends with: the frame's pass list, a queued values write, and a full solve.","description":"","source":{"file":"std/sim/fluids.ts","line":155}},"sim.fluids.FluidInitPart":{"id":"sim.fluids.FluidInitPart","name":"FluidInitPart","category":"sim.fluids","module":"std/sim/fluids.ts","kind":"interface","text":"interface FluidInitPart<TParams = unknown> {\n    setup?(ctx: FluidSetupCtx): void\n    /** Return true when the field must be (re)seeded this frame. */\n    stale(f: FluidFrame): boolean\n    run(f: FluidFrame, io: FluidInitIo<TParams>): void\n}","summary":"How the field starts, and when it must start over.","description":"Build one with `seededFieldInit`.","source":{"file":"std/sim/fluids.ts","line":162}},"sim.fluids.FluidContainerParts":{"id":"sim.fluids.FluidContainerParts","name":"FluidContainerParts","category":"sim.fluids","module":"std/sim/fluids.ts","kind":"interface","text":"interface FluidContainerParts {\n    /** The pass that writes the inside/outside mask, run after the values write and before injection. */\n    maskPass: KitComputePipeline\n    /** Extra outputs for the fragment. */\n    // The volumetric field texture.\n    outputs?: Record<string, unknown>\n    /** Passes that run first each frame, even on a frame too short to solve. */\n    // The volumetric field pre-march. They run FIRST, and they are what a sub-millisecond frame\n    // still returns — a pending march is never dropped.\n    preFrame?(frameParams: unknown): ComputeStep[] | null\n}","summary":"What a container (a shape the fluid is confined to) contributes each frame.","description":"","source":{"file":"std/sim/fluids.ts","line":171}},"sim.fluids.FluidSimConfig":{"id":"sim.fluids.FluidSimConfig","name":"FluidSimConfig","category":"sim.fluids","module":"std/sim/fluids.ts","kind":"interface","text":"interface FluidSimConfig<TParams> {\n    /** Cells per side of the grid. 256 is the usual choice. */\n    // Structural — the kernels are baked against it.\n    resolution: number\n    /** The layout naming the fluid's buffers. It must use the solver's entry names. */\n    // The scaffolds/fluids entry-name contract: velA, velB, dyeA, dyeB, pressure, divergence, params (+ maskBuf).\n    layout: unknown\n    /** The layout of the output pass: the dye buffer read-only plus the output texture. */\n    // `dyeA` read-only + `outTex`.\n    outputLayout: unknown\n    /** The schema of the per-frame values your passes read. The engine owns the buffer. */\n    paramsSchema: unknown\n    /** The solver's passes and how many pressure iterations to run per solve. 10 is the norm. */\n    solver: {kernels: StableFluidsKernels; jacobiIters: number}\n    /** The pass that turns dye into the output texture, and the name the fragment reads it under. */\n    output: {kernel: FluidKernel; key: string}\n    /** How the values reach the GPU: written at once (default), or queued in order with the passes. Use `'thunk'` with `seededFieldInit`, whose warm-up queues its own writes. */\n    write?: 'direct' | 'thunk'\n    /** Track the pointer for a cursor force. Stroke emitters built with `cursorRibbon` track their own. */\n    pointer?: PointerVelocityTrackerOptions\n    /** A shape the fluid is confined to. */\n    // Allocates `maskBuf`, dispatches its mask pass every frame.\n    container?: {setup(ctx: FluidSetupCtx): FluidContainerParts}\n    /** How the field starts. Omit to start empty. */\n    init?: FluidInitPart<TParams>\n    /** What injects dye and momentum each frame. */\n    inject?: FluidEmitter<TParams>[]\n    /** Passes run before and after every solve. */\n    solve?: FluidSolveStage[]\n    /** The values your passes read this frame, built from the clock, the pointer and your props. */\n    // The per-frame params derivation — the one runtime surface.\n    values(f: FluidFrame): TParams\n    /** Longest frame step accepted, in seconds. Default 0.033. */\n    maxDeltaTime?: number\n}","summary":"What a fluid simulation declares: its grid, its solver, its output, its parts and its per-frame values.","description":"","source":{"file":"std/sim/fluids.ts","line":184}},"sim.fluids.CursorRibbonSpec":{"id":"sim.fluids.CursorRibbonSpec","name":"CursorRibbonSpec","category":"sim.fluids","module":"std/sim/fluids.ts","kind":"interface","text":"interface CursorRibbonSpec<TPrepared> {\n    /** Distance between stamps, in uv. Half the brush radius reads continuous. */\n    stepSize: number\n    /** Most stamps in one frame, so a fast flick cannot run away. */\n    maxSteps: number\n    /** A per-stamp payload prepared in stamp order, with `t` from 0 to 1 along the stroke. */\n    // Advanced at BUILD time in stamp order (InkFlow's color cycle).\n    prepare?: (t: number) => TPrepared\n    /** Write the values for one stamp at `posX, posY` (grid cells) right before its pass runs. */\n    // Runs in a thunk right before that stamp's dispatch.\n    write: (posX: number, posY: number, t: number, prepared: TPrepared) => void\n}","summary":"This frame's ribbon of stamps along the stroke: spacing, cap, and how each stamp is written.","description":"","source":{"file":"std/sim/fluids.ts","line":381}},"sim.feedback.FeedbackRoot":{"id":"sim.feedback.FeedbackRoot","name":"FeedbackRoot","category":"sim.feedback","module":"std/sim/feedback.ts","kind":"type","text":"type FeedbackRoot = NonNullable<GpuFragmentParams['gpu']>['root']","summary":"The GPU device handle the build callback receives, for creating the resources your step reads.","description":"","source":{"file":"std/sim/feedback.ts","line":43}},"sim.feedback.FeedbackKernel":{"id":"sim.feedback.FeedbackKernel","name":"FeedbackKernel","category":"sim.feedback","module":"std/sim/feedback.ts","kind":"type","text":"type FeedbackKernel = (cx: number, cy: number) => void","summary":"A step function run once per state cell, with the cell's column and row.","description":"","source":{"file":"std/sim/feedback.ts","line":47}},"sim.feedback.FeedbackBindContext":{"id":"sim.feedback.FeedbackBindContext","name":"FeedbackBindContext","category":"sim.feedback","module":"std/sim/feedback.ts","kind":"type","text":"type FeedbackBindContext = SimBindContext & {paramsBuffer: unknown}","summary":"What you get when wiring your layout: the child's picture, the display texture, any extra textures you declared, and the buffer holding your per-frame values.","description":"","source":{"file":"std/sim/feedback.ts","line":53}},"sim.feedback.FeedbackSimConfig":{"id":"sim.feedback.FeedbackSimConfig","name":"FeedbackSimConfig","category":"sim.feedback","module":"std/sim/feedback.ts","kind":"interface","text":"interface FeedbackSimConfig<TParams, TGroups> {\n    /** Cells per side of the square state texture. 512–768 reads sharp on most canvases. */\n    // State + display resolution per axis (square — both current consumers' shape).\n    size: number\n    /** Storage precision. `rgba16float` is enough for color; `rgba32float` for exact positions. */\n    format: 'rgba16float' | 'rgba32float'\n    /** Extra textures that keep a previous and a current side, swapped together with the state. */\n    // TimeTrail's prev-live copy. A `[1, 1]` stub fills a slot a disabled kernel variant never touches.\n    extraSlots?: Record<string, SimSize>\n    /** Extra textures with no previous side. */\n    textures?: Record<string, SimSize>\n    /** A prop that scales the clock. 0 freezes the simulation, 2 runs it twice as fast. */\n    speedProp?: string\n    /** Longest frame step accepted, in seconds. Default 0.05. Keeps a tab switch from jumping the state. */\n    maxDeltaTime?: number\n    /** The schema of the per-frame values your step reads. The engine owns the buffer. */\n    paramsSchema: unknown\n    /**\n     * Wire your layout for one direction of the swap: `read` is last frame's state, `write` is\n     * this frame's. Called twice, once per direction, after the child's picture exists.\n     */\n    bindGroups: (ctx: FeedbackBindContext, read: SimSlot, write: SimSlot) => TGroups\n    /** Wire resources that do not depend on the swap direction. Called once. */\n    staticGroups?: (ctx: FeedbackBindContext) => unknown\n    /**\n     * The rule: one step function that reads `read`, writes `write` and the display copy.\n     * Pick the variant inside the build callback when a compile-time prop changes it.\n     */\n    // ONE kernel per step, compile-time variant selected in the build callback. Splitting a step\n    // into several kernels is a different simulation (and different WGSL); a shader that needs a\n    // multi-dispatch step should not ride this noun's default frame.\n    step: FeedbackKernel\n    /** Return true to skip this frame. The last state stays on screen and nothing swaps. */\n    skip?: (t: SimTick<TGroups, unknown>) => boolean\n    /** The values your step reads this frame, built from the clock and your props. */\n    // The per-frame params derivation — the one runtime surface. Written before the dispatch.\n    values: (t: SimTick<TGroups, unknown>) => TParams\n    /** Extra outputs for the fragment beyond `childTexture` and `display`. */\n    outputs?: Record<string, unknown>\n}","summary":"What a feedback simulation declares: the state's size and format, one step function, the wiring that hands it the read and write sides, and the values it reads each frame.","description":"","source":{"file":"std/sim/feedback.ts","line":59}},"sim.agents.RenderAgentsPipelines":{"id":"sim.agents.RenderAgentsPipelines","name":"RenderAgentsPipelines","category":"sim.agents","module":"std/sim/agents.ts","kind":"interface","text":"interface RenderAgentsPipelines {\n    splat: AgentPipelineSpec\n    resolve: AgentPipelineSpec\n}","summary":"The two draw steps every agent frame ends with: `splat` draws the agents, `resolve` turns the drawing into the output picture.","description":"","source":{"file":"std/sim/agents.ts","line":46}},"sim.agents.AgentSimBaked":{"id":"sim.agents.AgentSimBaked","name":"AgentSimBaked","category":"sim.agents","module":"std/sim/agents.ts","kind":"interface","text":"interface AgentSimBaked {\n    /** Every step by name: the spawn, the physics, the splat, the resolve. */\n    pipelines: Record<string, AgentPipelineSpec>\n    /** The steps that run each frame, in order (keys of `pipelines`). */\n    program: readonly string[]\n    /** The spawn step, run once before the first frame and again when `reseed` changes. */\n    initStep?: string\n    /** Override the output size decided at composition (an aspect-fitted target). */\n    outputSize?: [number, number]\n    /** A second resource group for every step marked `extra` (a shape's own distance field). */\n    extraBindGroup?: unknown\n    /** Resources ready now for the keys in `externalKeys` (a solver's texture created in `bake`). */\n    bindNow?: Record<string, unknown>\n    /** Anything `frame` needs from composition: a solver, a per-shape resolver. */\n    setup?: unknown\n}","summary":"What `bake` returns: the steps, their order, and anything the frame needs from composition.","description":"","source":{"file":"std/sim/agents.ts","line":209}},"sim.agents.AgentSimFragmentConfig":{"id":"sim.agents.AgentSimFragmentConfig","name":"AgentSimFragmentConfig","category":"sim.agents","module":"std/sim/agents.ts","kind":"interface","text":"interface AgentSimFragmentConfig {\n    /** The output the fragment samples over the full canvas. */\n    // rgba16f is filterable — one bilinear tap.\n    output: string\n    /** What to show without a GPU: nothing, or the child untouched. */\n    fallback: 'transparent' | 'child'\n    /** Combine the sampled picture with something else, such as laying it over the child. */\n    compose?: (field: Expr, params: GpuFragmentParams) => Expr\n}","summary":"How the layer samples the simulation's picture.","description":"","source":{"file":"std/sim/agents.ts","line":227}},"sim.agents.AgentSimConfig":{"id":"sim.agents.AgentSimConfig","name":"AgentSimConfig","category":"sim.agents","module":"std/sim/agents.ts","kind":"interface","text":"interface AgentSimConfig<TParams extends AnyWgslStruct> {\n    /** The layout naming the per-agent buffers, the accumulators, the values and the output texture. */\n    layout: AgentSystemConfig<TParams>['layout']\n    /** The schema of the per-frame values the steps read. */\n    params: TParams\n    /** How many agents the buffers hold. The count prop can be anything up to it. */\n    maxAgents: number\n    /** A lower effective count on phones and tablets. The prop's range is unchanged. */\n    countCap?: DeviceTier\n    /** The output texture: its layout key, the name the fragment reads it under, its size and format. */\n    output: AgentSystemConfig<TParams>['output']\n    /** Layout keys filled later, through `childTexture` or `bake.bindNow`. */\n    externalKeys?: readonly string[]\n    /** Read the child as a texture and bind it to this layout key once it exists. */\n    // ParticleField.\n    childTexture?: {externalKey: string}\n    /** Assemble the steps once per composition, from compile-time props. Return null to run nothing. */\n    bake: (params: GpuFragmentParams) => AgentSimBaked | null\n    /** Build the once-per-frame program: write the values, then return `sys.frame(...)`. */\n    // Usually via `sys.frame(...)`, prepending any extra passes the shader owns.\n    frame: (\n        sys: AgentSystem<TParams>,\n        params: GpuFragmentParams,\n        setup: unknown,\n    ) => (frameParams: unknown) => ComputeStep[] | null\n    fragment: AgentSimFragmentConfig\n}","summary":"What an agent simulation declares: its state layout, its values, its count, its output, and the bake, frame and fragment halves.","description":"","source":{"file":"std/sim/agents.ts","line":238}},"sim.agents.AgentSimSpread":{"id":"sim.agents.AgentSimSpread","name":"AgentSimSpread","category":"sim.agents","module":"std/sim/agents.ts","kind":"interface","text":"interface AgentSimSpread {\n    compute: GpuComputeNode\n    gpu: {fragment: (params: GpuFragmentParams) => Expr}\n}","summary":"The two halves `agentSim` adds to a definition: the simulation and the fragment that samples it.","description":"","source":{"file":"std/sim/agents.ts","line":268}},"sim.agentForces.Vec4StateArray":{"id":"sim.agentForces.Vec4StateArray","name":"Vec4StateArray","category":"sim.agentForces","module":"std/sim/agentForces.ts","kind":"type","text":"type Vec4StateArray = d.v4f[]","summary":"A per-agent buffer of four numbers each (`agents`, `pos`, `vel`, …).","description":"","source":{"file":"std/sim/agentForces.ts","line":41}},"sim.agentForces.AgentForce3":{"id":"sim.agentForces.AgentForce3","name":"AgentForce3","category":"sim.agentForces","module":"std/sim/agentForces.ts","kind":"type","text":"type AgentForce3 = (pos: d.v3f, vel: d.v3f, seed: number) => d.v3f","summary":"A push on a 3D agent, from its position, velocity and per-agent seed.","description":"","source":{"file":"std/sim/agentForces.ts","line":50}},"sim.agentForces.ShapeField3":{"id":"sim.agentForces.ShapeField3","name":"ShapeField3","category":"sim.agentForces","module":"std/sim/agentForces.ts","kind":"type","text":"type ShapeField3 = (pos: d.v3f) => number","summary":"The signed distance to a shape's surface at a shape-local 3D point, negative inside.","description":"Build one with `shapeField`.","source":{"file":"std/sim/agentForces.ts","line":53}},"sim.agentForces.VolumeSwarmLayout":{"id":"sim.agentForces.VolumeSwarmLayout","name":"VolumeSwarmLayout","category":"sim.agentForces","module":"std/sim/agentForces.ts","kind":"interface","text":"interface VolumeSwarmLayout {\n    readonly $: {\n        readonly pos: Vec4StateArray\n        readonly vel: Vec4StateArray\n        readonly dens: AtomicU32[]\n        readonly params: VolumeParamsView\n    }\n}","summary":"What a 3D swarm's layout must name: `pos`, `vel`, the `dens` grid and the `params` values.","description":"","source":{"file":"std/sim/agentForces.ts","line":77}},"sim.agentForces.PressureForce":{"id":"sim.agentForces.PressureForce","name":"PressureForce","category":"sim.agentForces","module":"std/sim/agentForces.ts","kind":"interface","text":"interface PressureForce {\n    force: AgentForce3\n    /** Clears the density grid. Dispatch it `fixed` over `cells`, first in the program. */\n    clearKernel: (i: number) => void\n    /** Adds each agent to the density grid. Dispatch it over `agents`, before the update. */\n    splatKernel: (i: number) => void\n    /** The density grid's cell count (the clear dispatch size). */\n    cells: number\n}","summary":"What `force.pressure` returns: the force, plus the two density-grid steps and the grid's cell count for the program.","description":"","source":{"file":"std/sim/agentForces.ts","line":87}},"sim.agentForces.AgentSteer2":{"id":"sim.agentForces.AgentSteer2","name":"AgentSteer2","category":"sim.agentForces","module":"std/sim/agentForces.ts","kind":"type","text":"type AgentSteer2 = (pos: d.v2f, vel: d.v2f, maxSpeedI: number) => d.v2f","summary":"A steering push on a 2D agent, from its position, velocity and its own top speed.","description":"","source":{"file":"std/sim/agentForces.ts","line":393}},"sim.agentForces.SteeringLayout":{"id":"sim.agentForces.SteeringLayout","name":"SteeringLayout","category":"sim.agentForces","module":"std/sim/agentForces.ts","kind":"interface","text":"interface SteeringLayout {\n    readonly $: {\n        readonly agents: Vec4StateArray\n        readonly agit: number[]\n        readonly params: SteeringParamsView\n    }\n}","summary":"What a flock's layout must name: `agents`, the `agit` excitement buffer and the `params` values.","description":"","source":{"file":"std/sim/agentForces.ts","line":417}},"sim.agentForces.FieldSampleValue":{"id":"sim.agentForces.FieldSampleValue","name":"FieldSampleValue","category":"sim.agentForces","module":"std/sim/agentForces.ts","kind":"type","text":"type FieldSampleValue = d.Infer<typeof FieldSample>","summary":"A field sample's value: `rhat`, `strength`, `phi`.","description":"","source":{"file":"std/sim/agentForces.ts","line":599}},"sim.agentForces.AgentTorque":{"id":"sim.agentForces.AgentTorque","name":"AgentTorque","category":"sim.agentForces","module":"std/sim/agentForces.ts","kind":"type","text":"type AgentTorque = (fld: FieldSampleValue, theta: number, fi: number) => number","summary":"A turn on a turning agent, from the field at its position, its angle and its index.","description":"","source":{"file":"std/sim/agentForces.ts","line":602}},"sim.agentForces.AgentFieldAt":{"id":"sim.agentForces.AgentFieldAt","name":"AgentFieldAt","category":"sim.agentForces","module":"std/sim/agentForces.ts","kind":"type","text":"type AgentFieldAt = (pos: d.v2f) => FieldSampleValue","summary":"A sample of a field at a 2D position.","description":"","source":{"file":"std/sim/agentForces.ts","line":605}},"sim.agentForces.AgentHome2":{"id":"sim.agentForces.AgentHome2","name":"AgentHome2","category":"sim.agentForces","module":"std/sim/agentForces.ts","kind":"type","text":"type AgentHome2 = (fi: number) => d.v2f","summary":"An agent's resting position, from its index.","description":"","source":{"file":"std/sim/agentForces.ts","line":608}},"sim.agentForces.OrientationLayout":{"id":"sim.agentForces.OrientationLayout","name":"OrientationLayout","category":"sim.agentForces","module":"std/sim/agentForces.ts","kind":"interface","text":"interface OrientationLayout {\n    readonly $: {\n        readonly agents: Vec4StateArray\n        readonly agit: number[]\n        readonly params: OrientationParamsView\n    }\n}","summary":"What a turning-agent layout must name: `agents`, the `agit` excitement buffer and the `params` values.","description":"","source":{"file":"std/sim/agentForces.ts","line":634}},"sim.agentForces.DriftLayout":{"id":"sim.agentForces.DriftLayout","name":"DriftLayout","category":"sim.agentForces","module":"std/sim/agentForces.ts","kind":"interface","text":"interface DriftLayout {\n    readonly $: {\n        readonly agents: Vec4StateArray\n        readonly params: DriftParamsView\n    }\n}","summary":"What a drifting-mote layout must name: `agents` and the `params` values.","description":"","source":{"file":"std/sim/agentForces.ts","line":797}},"sim.agentForces.AgentDrift":{"id":"sim.agentForces.AgentDrift","name":"AgentDrift","category":"sim.agentForces","module":"std/sim/agentForces.ts","kind":"type","text":"type AgentDrift = (fi: number) => d.v2f","summary":"A built-in velocity for an agent, from its index alone.","description":"","source":{"file":"std/sim/agentForces.ts","line":805}},"sim.agentForces.ReliefLayout":{"id":"sim.agentForces.ReliefLayout","name":"ReliefLayout","category":"sim.agentForces","module":"std/sim/agentForces.ts","kind":"interface","text":"interface ReliefLayout {\n    readonly $: {\n        readonly pos: Vec4StateArray\n        readonly vel: Vec4StateArray\n        readonly col: Vec4StateArray\n        readonly src: d.Infer<d.WgslTexture2d<d.F32>>\n        readonly params: ReliefParamsView\n    }\n}","summary":"What an image-relief layout must name: `pos`, `vel`, `col`, the child picture `src` and the `params` values.","description":"","source":{"file":"std/sim/agentForces.ts","line":910}},"sim.agentForces.AgentChannel":{"id":"sim.agentForces.AgentChannel","name":"AgentChannel","category":"sim.agentForces","module":"std/sim/agentForces.ts","kind":"type","text":"type AgentChannel = (rgb: d.v3f, a: number) => number","summary":"One number in 0–1 read out of a color and its alpha: a brightness, a channel, a saturation.","description":"","source":{"file":"std/sim/agentForces.ts","line":922}},"sim.agentForces.AdvectLayout":{"id":"sim.agentForces.AdvectLayout","name":"AdvectLayout","category":"sim.agentForces","module":"std/sim/agentForces.ts","kind":"interface","text":"interface AdvectLayout {\n    readonly $: {\n        readonly agents: Vec4StateArray\n        readonly velTex: d.Infer<d.WgslTexture2d<d.F32>>\n        readonly params: AdvectParamsView\n    }\n}","summary":"What a fluid-carried layout must name: `agents`, the fluid's velocity texture `velTex` and the `params` values.","description":"","source":{"file":"std/sim/agentForces.ts","line":1159}},"sim.agentRender.WorldSplatLayout":{"id":"sim.agentRender.WorldSplatLayout","name":"WorldSplatLayout","category":"sim.agentRender","module":"std/sim/agentRender.ts","kind":"interface","text":"interface WorldSplatLayout {\n    readonly $: {\n        readonly agents: Vec4StateArray\n        readonly accumE: AtomicU32[]\n        readonly accumS: AtomicU32[]\n        readonly params: {\n            readonly aspect: number\n            readonly bodyR: number\n        }\n    }\n}","summary":"What an oriented splat's layout must name: `agents`, the `accumE` and `accumS` canvases, and `params.aspect` and `params.bodyR`.","description":"","source":{"file":"std/sim/agentRender.ts","line":39}},"sim.agentRender.RampResolveLayout":{"id":"sim.agentRender.RampResolveLayout","name":"RampResolveLayout","category":"sim.agentRender","module":"std/sim/agentRender.ts","kind":"interface","text":"interface RampResolveLayout {\n    readonly $: {\n        readonly accumE: AtomicU32[]\n        readonly accumS: AtomicU32[]\n        readonly outTex: StorageTex\n        readonly params: {\n            readonly colA: d.v4f\n            readonly colB: d.v4f\n        }\n    }\n}","summary":"What a ramp resolve's layout must name: the two canvases, the output texture `outTex`, and the two colors `params.colA` and `params.colB`.","description":"","source":{"file":"std/sim/agentRender.ts","line":59}},"sim.agentRender.OrientedWorldSplatConfig":{"id":"sim.agentRender.OrientedWorldSplatConfig","name":"OrientedWorldSplatConfig","category":"sim.agentRender","module":"std/sim/agentRender.ts","kind":"interface","text":"interface OrientedWorldSplatConfig {\n    /** The shape name: `arrow`, `streak`, `square`, `dot`, `glow`. */\n    shape: string\n    /** The canvas side in texels. */\n    res: number\n    /** Largest body half-width accepted, in texels. A safety cap, not a size. */\n    // The window itself is dynamic.\n    splatRCap: number\n    /** Where the heading comes from: the agent's velocity, or a stored angle plus `home`. */\n    heading: 'velocity' | 'angle'\n    /** Where excitement comes from: a per-agent buffer, or speed over `params.speedNorm`. */\n    agitation: 'buffer' | 'speed'\n    /** Stretch a soft shape into a comet along the heading. */\n    comet: boolean\n    /** For `heading: 'angle'`: the resting position each stored offset is measured from. */\n    // MagneticFilings.\n    home?: AgentHome2\n    name: string\n}","summary":"How agents with a heading are drawn.","description":"","source":{"file":"std/sim/agentRender.ts","line":87}},"sim.agentRender.PointWorldSplatConfig":{"id":"sim.agentRender.PointWorldSplatConfig","name":"PointWorldSplatConfig","category":"sim.agentRender","module":"std/sim/agentRender.ts","kind":"interface","text":"interface PointWorldSplatConfig {\n    /** The shape name: `dot`, `glow`, `square`. */\n    shape: string\n    /** The canvas side in texels. */\n    res: number\n    /** Where the mote is drawn, from its stored position. */\n    place: (fi: number, stored: d.v2f) => d.v2f\n    /** How bright the mote is. */\n    brightness: (fi: number) => number\n    /** The mote's body radius in canvas heights, already floored. */\n    bodyRadius: (fi: number) => number\n    name: string\n}","summary":"How round motes are drawn: the shape, the canvas size, and the three `pointSlot` parts.","description":"","source":{"file":"std/sim/agentRender.ts","line":333}},"sim.agentRender.PointWorldSplatLayout":{"id":"sim.agentRender.PointWorldSplatLayout","name":"PointWorldSplatLayout","category":"sim.agentRender","module":"std/sim/agentRender.ts","kind":"interface","text":"interface PointWorldSplatLayout {\n    readonly $: {\n        readonly agents: Vec4StateArray\n        readonly accumE: AtomicU32[]\n        readonly params: {\n            readonly aspect: number\n            readonly softness: number\n        }\n    }\n}","summary":"What a point splat's layout must name: `agents`, the `accumE` canvas, and `params.aspect` and `params.softness`.","description":"","source":{"file":"std/sim/agentRender.ts","line":352}},"sim.agentRender.VolumeSplatConfig":{"id":"sim.agentRender.VolumeSplatConfig","name":"VolumeSplatConfig","category":"sim.agentRender","module":"std/sim/agentRender.ts","kind":"interface","text":"interface VolumeSplatConfig {\n    /** The shape name: `dot`, `glow`, `square`. */\n    shape: string\n    /** The output side in texels. */\n    outRes: number\n    /** Largest particle radius drawn, in texels. */\n    maxSplatSize: number\n    /** How far a particle's profile reaches, as distance² over size². 6 covers a soft glow. */\n    // Radial reject: a texel contributes while q = dist²/size² < extQ.\n    extQ: number\n    names: {splat: string; glow: string; profile: string}\n}","summary":"How a 3D swarm is drawn: the shape, the output size, the largest particle, and the reach of each particle's profile.","description":"","source":{"file":"std/sim/agentRender.ts","line":474}},"sim.agentRender.ReliefSplatConfig":{"id":"sim.agentRender.ReliefSplatConfig","name":"ReliefSplatConfig","category":"sim.agentRender","module":"std/sim/agentRender.ts","kind":"interface","text":"interface ReliefSplatConfig {\n    /** The shape name: `dot`, `glow`, `square`. */\n    shape: string\n    /** Largest body radius drawn, in texels. */\n    // The window itself is dynamic.\n    splatRCap: number\n    /** Width of the soft edge on hard shapes, in texels. 0.75 is typical. */\n    aaW: number\n    /** Child alpha below which a particle is not drawn at all. */\n    minAlpha: number\n    /** Precision of the color accumulation. 1024 is typical. */\n    // Fixed-point scale for the atomic accumulators.\n    fp: number\n    /** How fast particles breathe when idle, in cycles per second of `params.time`. */\n    wobbleFreq: number\n    /** How strongly depth changes size and spread. */\n    perspK: number\n    name: string\n}","summary":"How particles standing on an image are drawn.","description":"","source":{"file":"std/sim/agentRender.ts","line":595}},"sim.agentRender.RampResolveConfig":{"id":"sim.agentRender.RampResolveConfig","name":"RampResolveConfig","category":"sim.agentRender","module":"std/sim/agentRender.ts","kind":"interface","text":"interface RampResolveConfig {\n    /** The color space the two colors mix in, as the `colorSpace` prop's number. */\n    // Baked color-space mode for the rest→excited mix (the canonical mixColorsVariants path).\n    colorSpace: number\n    /** The canvas side in texels. */\n    res: number\n    /** The scale the splat accumulated at. `renderAgents` fills this in. */\n    // 1/255 for the world splats, 1/256 for the volume splat.\n    invGain: number\n    /** Multiply brightness by `params.exposure`. */\n    exposure?: boolean\n    /** Motion trails: `'on'` always, `'none'` never, or `{baked}` for a layer whose trails prop can be 0. */\n    // 'on' (always composited), {baked: boolean} (read baked out at trails = 0, write kept so\n    // scrubbing up never ghosts), or 'none' (no trail buffer in the layout).\n    trails: 'on' | 'none' | {baked: boolean}\n    name: string\n}","summary":"How the rest-to-excited color resolve behaves.","description":"","source":{"file":"std/sim/agentRender.ts","line":723}}},"coverage":{"words":575,"documented":575,"undocumented":[],"curatedCategories":["compose","define","effects.blurs","effects.color","effects.edgeGlow","effects.fracture","effects.instances","effects.lens","effects.overlay","effects.pointerFields","effects.reveal","effects.stylize","figures","filter","frames","gradients","invoke","light","lists","mask","materials","math","media","motion","noise","paint","patterns","radiance","shape","signal","sim.agentForces","sim.agentFrame","sim.agentRender","sim.agents","sim.feedback","sim.fluids","sim.grids","sim.shapeFields","simulate","slots","values","volume","voxels","warps"]}}