/** * The uniform ABI every shadertown shader shares. * * One struct serves the whole catalogue: seven controls that mean the same * thing everywhere (speed, scale, intensity, vibrancy, grain, vignette, warp), * fourteen generic slots each shader gives its own meaning, and three colours. * A shader can therefore be swapped at runtime without touching any binding. */ export type ParamSlot = "speed" | "scale" | "intensity" | "vibrancy" | "grain" | "vignette" | "warp" | "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n"; export type ColorSlot = "colorA" | "colorB" | "colorC"; export interface NumericControl { /** Which uniform field this control writes to. */ readonly slot: ParamSlot; readonly label: string; readonly min: number; readonly max: number; readonly step: number; readonly default: number; /** Optional sampling bounds; manual sliders keep their full range. */ readonly randomRange?: readonly [number, number]; /** One line explaining what moving this slider actually does. */ readonly hint?: string; } export interface ColorControl { readonly label: string; readonly default: string; readonly hint?: string; } /** * A curated colour trio for one shader. * * Representational shaders — an island, a beach, grassland — stop reading as * the thing they draw when their colours are rolled at random. A shader that * declares presets is asking Randomise to choose between these instead of * rolling hues; the chosen trio lands in the ordinary colour controls, where * it can still be overridden well by well. */ export interface ColorPreset { readonly name: string; readonly colorA: string; readonly colorB: string; readonly colorC: string; } export type ShaderFamily = "gradient" | "organic" | "geometric" | "fractal" | "spatial" | "texture" | "pixel" | "scene" | "optical" | "distortion" | "image" | "transition" | "compute" | "particles" | "drafting" | "light"; /** * Whether a shader invents its picture, reads one, simulates one, or renders * one through a multi-pass graph. * * A `source` shader binds a texture and two more slots on top of the shared * `Params` block, so it can refract, recolour or resample a layer that is * already there. A graph shader may bind the same three: ask `isSourceShader` * whether a shader wants a layer, and `shaderKind` how it draws. A `compute` shader carries state between frames: the runtime * allocates its storage buffers, steps its `@compute` entry points before the * draw, and the fragment stage reads the result. A `graph` shader is a whole * render graph in one module — a displacement field, an instanced particle * draw, a bloom chain and a present pass, each its own entry point — and the * runtime encodes those passes into the frame ahead of the present pass. The * extra bindings are additive either way: a `generative` shader declares none * of them and keeps the layout it has always had. * * Kind is decided by where the WGSL file lives, so it can never drift from the * bindings the shader actually declares. Read it with `shaderKind(name)`. */ export type ShaderKind = "generative" | "source" | "compute" | "graph"; /** Rough per-frame cost, used to pick sensible defaults for previews. */ export type ShaderCost = "light" | "medium" | "heavy"; export interface ShaderDefinition { readonly name: string; readonly tagline: string; readonly description: string; readonly family: ShaderFamily; readonly tags: readonly string[]; readonly cost: ShaderCost; readonly controls: Readonly>; readonly colors: Readonly>; /** * Curated colour trios. When present, Randomise picks one of these rather * than rolling hues, and the picker UI offers them as one-click starting * points. The first preset should match the colour defaults. */ readonly presets?: readonly ColorPreset[]; /** * For a `source` shader: which shader to render behind it when the caller * supplies no source of their own, so a preview is never a blank canvas. * * Typed as a plain string rather than `ShaderName`, because `ShaderName` is * derived from the registry that this interface describes. */ readonly defaultSource?: string; } /** Values written to the uniform block, in the order the WGSL struct declares. */ export interface ResolvedUniforms { resolution: [number, number]; pointer: [number, number]; time: number; speed: number; scale: number; intensity: number; vibrancy: number; grain: number; vignette: number; warp: number; a: number; b: number; c: number; d: number; /** 1 on the High tier, 0 on Low. Shaders read it through `st_steps`. */ quality: number; e: number; f: number; g: number; colorA: [number, number, number, number]; colorB: [number, number, number, number]; colorC: [number, number, number, number]; h: number; i: number; j: number; k: number; l: number; m: number; n: number; } /** When the renderer is allowed to animate. */ export type PlayMode = "always" | "visible" | "hover" | "never"; /** * How much a frame is allowed to cost. * * Two tiers, not a dial. Every shader draws the same picture on both; Low * clamps the device pixel ratio to 1 and halves the iteration counts the * shader marked as cost rather than look — march steps, octaves, taps, rays. * The scene owns what that means, through `st_steps` in its WGSL. */ export type QualityTier = "high" | "low"; /** * What the caller asked for. `auto` starts on High and drops to Low once, if * the device class, the battery, or the presented frame rate say so. It never * climbs back: a picture that flips between two resolutions reads as broken, * and the reader who wants High back can ask for it. */ export type QualityPreference = "auto" | QualityTier; /** Which advisory signal asked for Low. */ export type DowngradeReason = "device" | "battery" | "frame-health"; /** Why the tier on screen is the tier on screen. */ export type QualityReason = "initial" | "forced" | DowngradeReason; /** What was asked for, what is on screen, and why they differ if they do. */ export interface QualityState { readonly preference: QualityPreference; readonly effective: QualityTier; readonly reason: QualityReason; } export interface QualityOptions { /** * Device-pixel-ratio clamp, `[min, max]`. Lowering the ceiling is the * cheapest way to make a heavy shader affordable on a retina display. */ readonly dpr?: readonly [number, number]; /** Frames per second cap. `0` means "every animation frame". */ readonly fps?: number; /** * Which tier to draw. `auto` (the default) starts on High and downgrades * once when the page's signals ask for it; `high` and `low` pin the tier. * On Low the `dpr` ceiling is clamped to 1 as well. */ readonly tier?: QualityPreference; } //# sourceMappingURL=types.d.ts.map