shadertown 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +20 -0
- package/cli/cli.mjs +341 -0
- package/cli/shadertown.mjs +8 -0
- package/dist/config.d.ts +29 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +8 -0
- package/dist/config.js.map +1 -0
- package/dist/export.d.ts +30 -0
- package/dist/export.d.ts.map +1 -0
- package/dist/export.js +234 -0
- package/dist/export.js.map +1 -0
- package/dist/generated/sources.d.ts +22 -0
- package/dist/generated/sources.d.ts.map +1 -0
- package/dist/generated/sources.js +24 -0
- package/dist/generated/sources.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/params.d.ts +24 -0
- package/dist/params.d.ts.map +1 -0
- package/dist/params.js +105 -0
- package/dist/params.js.map +1 -0
- package/dist/react.d.ts +41 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +127 -0
- package/dist/react.js.map +1 -0
- package/dist/registry.d.ts +1930 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +1067 -0
- package/dist/registry.js.map +1 -0
- package/dist/renderer.d.ts +31 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/renderer.js +144 -0
- package/dist/renderer.js.map +1 -0
- package/dist/stage.d.ts +39 -0
- package/dist/stage.d.ts.map +1 -0
- package/dist/stage.js +79 -0
- package/dist/stage.js.map +1 -0
- package/dist/types.d.ts +71 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +85 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// GENERATED by scripts/build-shaders.mjs — do not edit by hand.
|
|
2
|
+
// Each entry is the shared prelude plus one shader, ready to hand to vgpu's
|
|
3
|
+
// `effect()`. Shipping strings means consumers need no WGSL loader.
|
|
4
|
+
export const shaderSources = {
|
|
5
|
+
"aurora": "// shadertown shared prelude — prepended to every shader at build time.\n// One uniform block is the whole ABI: every shader reads the same struct,\n// so the runtime can swap shaders without changing a binding.\n\nstruct Params {\n resolution : vec2f,\n pointer : vec2f,\n time : f32,\n speed : f32,\n scale : f32,\n intensity : f32,\n vibrancy : f32,\n grain : f32,\n vignette : f32,\n warp : f32,\n a : f32,\n b : f32,\n c : f32,\n d : f32,\n colorA : vec4f,\n colorB : vec4f,\n colorC : vec4f,\n}\n\n@group(0) @binding(0) var<uniform> params : Params;\n\nconst PI : f32 = 3.141592653589793;\nconst TAU : f32 = 6.283185307179586;\n\nfn st_time() -> f32 {\n return params.time * params.speed;\n}\n\n// Centred, aspect-corrected coordinates. y stays in [-0.5, 0.5].\nfn st_centered(uv : vec2f) -> vec2f {\n let aspect = params.resolution.x / max(params.resolution.y, 1.0);\n return vec2f((uv.x - 0.5) * aspect, uv.y - 0.5);\n}\n\nfn st_aspect() -> f32 {\n return params.resolution.x / max(params.resolution.y, 1.0);\n}\n\nfn st_rot(angle : f32) -> mat2x2f {\n let s = sin(angle);\n let c = cos(angle);\n return mat2x2f(c, -s, s, c);\n}\n\n// --- hashing ---------------------------------------------------------------\n\nfn st_hash11(x : f32) -> f32 {\n var p = fract(x * 0.1031);\n p = p * (p + 33.33);\n return fract(p * (p + p));\n}\n\nfn st_hash21(p : vec2f) -> f32 {\n var p3 = fract(vec3f(p.xyx) * 0.1031);\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn st_hash22(p : vec2f) -> vec2f {\n var p3 = fract(vec3f(p.xyx) * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\nfn st_hash33(p : vec3f) -> vec3f {\n var p3 = fract(p * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.xxy + p3.yxx) * p3.zyx);\n}\n\n// --- noise -----------------------------------------------------------------\n\nfn st_noise2(p : vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = st_hash21(i);\n let b = st_hash21(i + vec2f(1.0, 0.0));\n let c = st_hash21(i + vec2f(0.0, 1.0));\n let d = st_hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfn st_noise3(p : vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let n000 = st_hash33(i + vec3f(0.0, 0.0, 0.0)).x;\n let n100 = st_hash33(i + vec3f(1.0, 0.0, 0.0)).x;\n let n010 = st_hash33(i + vec3f(0.0, 1.0, 0.0)).x;\n let n110 = st_hash33(i + vec3f(1.0, 1.0, 0.0)).x;\n let n001 = st_hash33(i + vec3f(0.0, 0.0, 1.0)).x;\n let n101 = st_hash33(i + vec3f(1.0, 0.0, 1.0)).x;\n let n011 = st_hash33(i + vec3f(0.0, 1.0, 1.0)).x;\n let n111 = st_hash33(i + vec3f(1.0, 1.0, 1.0)).x;\n let x00 = mix(n000, n100, u.x);\n let x10 = mix(n010, n110, u.x);\n let x01 = mix(n001, n101, u.x);\n let x11 = mix(n011, n111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\nfn st_fbm2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.5);\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise2(shifted);\n shifted = rotation * shifted * 2.02;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\nfn st_fbm3(p : vec3f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise3(shifted);\n shifted = shifted * 2.03;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// Ridged noise — sharp crests instead of soft hills.\nfn st_ridge2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.7);\n for (var i = 0; i < octaves; i = i + 1) {\n let n = 1.0 - abs(st_noise2(shifted) * 2.0 - 1.0);\n value = value + amplitude * n * n;\n shifted = rotation * shifted * 2.11;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// --- colour ----------------------------------------------------------------\n\n// Three-stop ramp built from the user's colours. t is clamped to [0, 1].\nfn st_palette(t : f32) -> vec3f {\n let x = clamp(t, 0.0, 1.0);\n let low = mix(params.colorA.rgb, params.colorB.rgb, smoothstep(0.0, 0.5, x));\n return mix(low, params.colorC.rgb, smoothstep(0.5, 1.0, x));\n}\n\n// Same ramp, but wrapping — good for cycling and polar shaders.\nfn st_paletteCyclic(t : f32) -> vec3f {\n let x = fract(t);\n let tri = 1.0 - abs(x * 2.0 - 1.0);\n return st_palette(tri);\n}\n\nfn st_luma(c : vec3f) -> f32 {\n return dot(c, vec3f(0.2126, 0.7152, 0.0722));\n}\n\nfn st_saturation(c : vec3f, amount : f32) -> vec3f {\n return mix(vec3f(st_luma(c)), c, amount);\n}\n\nfn st_tonemap(c : vec3f) -> vec3f {\n let x = max(c, vec3f(0.0));\n return (x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14);\n}\n\n// --- shared post chain -----------------------------------------------------\n// Every shader ends here, so vibrancy / grain / vignette behave identically\n// across the whole catalogue.\nfn st_post(color : vec3f, uv : vec2f) -> vec4f {\n var c = st_saturation(max(color, vec3f(0.0)), params.vibrancy);\n\n if (params.grain > 0.0) {\n let seed = uv * params.resolution + vec2f(params.time * 61.7, params.time * 37.3);\n let n = st_hash21(floor(seed));\n c = c + (n - 0.5) * params.grain;\n }\n\n if (params.vignette > 0.0) {\n let d = length(uv - vec2f(0.5)) * 1.41421356;\n c = c * (1.0 - params.vignette * smoothstep(0.22, 1.0, d));\n }\n\n return vec4f(clamp(c, vec3f(0.0), vec3f(1.0)), 1.0);\n}\n\n// ---------------------------------------------------------------------------\n// aurora\n// ---------------------------------------------------------------------------\n\n// Aurora — layered curtains of domain-warped noise over a night sky.\n\nfn curtain(p : vec2f, offset : f32, t : f32) -> f32 {\n var q = p;\n q.x = q.x + st_fbm2(p * vec2f(1.4, 0.7) + vec2f(offset, t * 0.25), 4) * params.warp;\n let band = st_fbm2(vec2f(q.x * 2.2 + offset * 3.0, q.y * 0.6 - t * 0.4), 5);\n let ribbon = pow(clamp(band, 0.0, 1.0), 2.2);\n let falloff = smoothstep(0.62, -0.12, p.y);\n let top = smoothstep(-0.55, -0.05, p.y);\n return ribbon * falloff * top;\n}\n\n@fragment fn fs_main(@location(0) uv : vec2f) -> @location(0) vec4f {\n let p = st_centered(uv) * params.scale;\n let t = st_time();\n\n // Night sky gradient, darkest at the horizon.\n var col = mix(vec3f(0.004, 0.008, 0.028), vec3f(0.02, 0.03, 0.07), smoothstep(1.0, -0.2, uv.y));\n\n // Stars, thinned out towards the aurora.\n let starCell = floor(uv * params.resolution / 3.0);\n let star = st_hash21(starCell);\n let twinkle = 0.6 + 0.4 * sin(t * 2.0 + star * TAU);\n col = col + vec3f(step(0.9985, star) * twinkle * params.d);\n\n // Three curtains at different depths.\n let layers = 3.0;\n var glow = 0.0;\n var tint = vec3f(0.0);\n for (var i = 0; i < 3; i = i + 1) {\n let fi = f32(i);\n let depth = 1.0 - fi / layers * 0.55;\n let amount = curtain(p * (1.0 + fi * 0.35), fi * 7.31, t) * depth;\n glow = glow + amount;\n tint = tint + st_palette(fi / (layers - 1.0)) * amount;\n }\n\n let energy = glow * params.intensity;\n col = col + tint * params.intensity;\n col = col + st_palette(0.5) * pow(energy, 3.0) * 0.35 * params.b;\n\n return st_post(st_tonemap(col * params.a), uv);\n}\n",
|
|
6
|
+
"bokeh": "// shadertown shared prelude — prepended to every shader at build time.\n// One uniform block is the whole ABI: every shader reads the same struct,\n// so the runtime can swap shaders without changing a binding.\n\nstruct Params {\n resolution : vec2f,\n pointer : vec2f,\n time : f32,\n speed : f32,\n scale : f32,\n intensity : f32,\n vibrancy : f32,\n grain : f32,\n vignette : f32,\n warp : f32,\n a : f32,\n b : f32,\n c : f32,\n d : f32,\n colorA : vec4f,\n colorB : vec4f,\n colorC : vec4f,\n}\n\n@group(0) @binding(0) var<uniform> params : Params;\n\nconst PI : f32 = 3.141592653589793;\nconst TAU : f32 = 6.283185307179586;\n\nfn st_time() -> f32 {\n return params.time * params.speed;\n}\n\n// Centred, aspect-corrected coordinates. y stays in [-0.5, 0.5].\nfn st_centered(uv : vec2f) -> vec2f {\n let aspect = params.resolution.x / max(params.resolution.y, 1.0);\n return vec2f((uv.x - 0.5) * aspect, uv.y - 0.5);\n}\n\nfn st_aspect() -> f32 {\n return params.resolution.x / max(params.resolution.y, 1.0);\n}\n\nfn st_rot(angle : f32) -> mat2x2f {\n let s = sin(angle);\n let c = cos(angle);\n return mat2x2f(c, -s, s, c);\n}\n\n// --- hashing ---------------------------------------------------------------\n\nfn st_hash11(x : f32) -> f32 {\n var p = fract(x * 0.1031);\n p = p * (p + 33.33);\n return fract(p * (p + p));\n}\n\nfn st_hash21(p : vec2f) -> f32 {\n var p3 = fract(vec3f(p.xyx) * 0.1031);\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn st_hash22(p : vec2f) -> vec2f {\n var p3 = fract(vec3f(p.xyx) * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\nfn st_hash33(p : vec3f) -> vec3f {\n var p3 = fract(p * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.xxy + p3.yxx) * p3.zyx);\n}\n\n// --- noise -----------------------------------------------------------------\n\nfn st_noise2(p : vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = st_hash21(i);\n let b = st_hash21(i + vec2f(1.0, 0.0));\n let c = st_hash21(i + vec2f(0.0, 1.0));\n let d = st_hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfn st_noise3(p : vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let n000 = st_hash33(i + vec3f(0.0, 0.0, 0.0)).x;\n let n100 = st_hash33(i + vec3f(1.0, 0.0, 0.0)).x;\n let n010 = st_hash33(i + vec3f(0.0, 1.0, 0.0)).x;\n let n110 = st_hash33(i + vec3f(1.0, 1.0, 0.0)).x;\n let n001 = st_hash33(i + vec3f(0.0, 0.0, 1.0)).x;\n let n101 = st_hash33(i + vec3f(1.0, 0.0, 1.0)).x;\n let n011 = st_hash33(i + vec3f(0.0, 1.0, 1.0)).x;\n let n111 = st_hash33(i + vec3f(1.0, 1.0, 1.0)).x;\n let x00 = mix(n000, n100, u.x);\n let x10 = mix(n010, n110, u.x);\n let x01 = mix(n001, n101, u.x);\n let x11 = mix(n011, n111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\nfn st_fbm2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.5);\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise2(shifted);\n shifted = rotation * shifted * 2.02;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\nfn st_fbm3(p : vec3f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise3(shifted);\n shifted = shifted * 2.03;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// Ridged noise — sharp crests instead of soft hills.\nfn st_ridge2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.7);\n for (var i = 0; i < octaves; i = i + 1) {\n let n = 1.0 - abs(st_noise2(shifted) * 2.0 - 1.0);\n value = value + amplitude * n * n;\n shifted = rotation * shifted * 2.11;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// --- colour ----------------------------------------------------------------\n\n// Three-stop ramp built from the user's colours. t is clamped to [0, 1].\nfn st_palette(t : f32) -> vec3f {\n let x = clamp(t, 0.0, 1.0);\n let low = mix(params.colorA.rgb, params.colorB.rgb, smoothstep(0.0, 0.5, x));\n return mix(low, params.colorC.rgb, smoothstep(0.5, 1.0, x));\n}\n\n// Same ramp, but wrapping — good for cycling and polar shaders.\nfn st_paletteCyclic(t : f32) -> vec3f {\n let x = fract(t);\n let tri = 1.0 - abs(x * 2.0 - 1.0);\n return st_palette(tri);\n}\n\nfn st_luma(c : vec3f) -> f32 {\n return dot(c, vec3f(0.2126, 0.7152, 0.0722));\n}\n\nfn st_saturation(c : vec3f, amount : f32) -> vec3f {\n return mix(vec3f(st_luma(c)), c, amount);\n}\n\nfn st_tonemap(c : vec3f) -> vec3f {\n let x = max(c, vec3f(0.0));\n return (x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14);\n}\n\n// --- shared post chain -----------------------------------------------------\n// Every shader ends here, so vibrancy / grain / vignette behave identically\n// across the whole catalogue.\nfn st_post(color : vec3f, uv : vec2f) -> vec4f {\n var c = st_saturation(max(color, vec3f(0.0)), params.vibrancy);\n\n if (params.grain > 0.0) {\n let seed = uv * params.resolution + vec2f(params.time * 61.7, params.time * 37.3);\n let n = st_hash21(floor(seed));\n c = c + (n - 0.5) * params.grain;\n }\n\n if (params.vignette > 0.0) {\n let d = length(uv - vec2f(0.5)) * 1.41421356;\n c = c * (1.0 - params.vignette * smoothstep(0.22, 1.0, d));\n }\n\n return vec4f(clamp(c, vec3f(0.0), vec3f(1.0)), 1.0);\n}\n\n// ---------------------------------------------------------------------------\n// bokeh\n// ---------------------------------------------------------------------------\n\n// Bokeh — out-of-focus lights drifting through depth. Additive, soft-edged.\n\nfn bokehLayer(p : vec2f, depth : f32, t : f32) -> vec3f {\n let density = mix(2.5, 7.0, depth);\n let drift = vec2f(t * 0.06 * (1.0 - depth * 0.7), -t * 0.09 * (1.0 - depth * 0.5));\n let grid = p * density + drift * density;\n let base = floor(grid);\n let frac = fract(grid) - 0.5;\n\n var acc = vec3f(0.0);\n for (var y = -1; y <= 1; y = y + 1) {\n for (var x = -1; x <= 1; x = x + 1) {\n let neighbour = vec2f(f32(x), f32(y));\n let rnd = st_hash22(base + neighbour + depth * 91.7);\n if (rnd.x < 0.35) { continue; }\n\n let wobble = vec2f(sin(t * 0.5 + rnd.y * TAU), cos(t * 0.4 + rnd.x * TAU)) * 0.12;\n let centre = neighbour + rnd - 0.5 + wobble;\n let d = length(frac - centre);\n\n // A disc with a bright rim — how a real lens renders defocused points.\n let radius = (0.10 + rnd.y * 0.22) * params.a * mix(1.6, 0.5, depth);\n let disc = smoothstep(radius, radius * (1.0 - params.b), d);\n let rim = smoothstep(radius * (1.0 - params.b * 0.8), radius, d) * disc;\n\n let tint = st_palette(rnd.y);\n acc = acc + tint * (disc * 0.55 + rim * params.c) * mix(0.35, 1.0, 1.0 - depth);\n }\n }\n return acc;\n}\n\n@fragment fn fs_main(@location(0) uv : vec2f) -> @location(0) vec4f {\n let p = st_centered(uv) * params.scale;\n let t = st_time();\n\n var col = mix(params.colorA.rgb * 0.10, vec3f(0.008, 0.008, 0.02), uv.y);\n for (var i = 0; i < 4; i = i + 1) {\n col = col + bokehLayer(p, f32(i) / 3.0, t) * params.d;\n }\n\n return st_post(st_tonemap(col * params.intensity), uv);\n}\n",
|
|
7
|
+
"caustics": "// shadertown shared prelude — prepended to every shader at build time.\n// One uniform block is the whole ABI: every shader reads the same struct,\n// so the runtime can swap shaders without changing a binding.\n\nstruct Params {\n resolution : vec2f,\n pointer : vec2f,\n time : f32,\n speed : f32,\n scale : f32,\n intensity : f32,\n vibrancy : f32,\n grain : f32,\n vignette : f32,\n warp : f32,\n a : f32,\n b : f32,\n c : f32,\n d : f32,\n colorA : vec4f,\n colorB : vec4f,\n colorC : vec4f,\n}\n\n@group(0) @binding(0) var<uniform> params : Params;\n\nconst PI : f32 = 3.141592653589793;\nconst TAU : f32 = 6.283185307179586;\n\nfn st_time() -> f32 {\n return params.time * params.speed;\n}\n\n// Centred, aspect-corrected coordinates. y stays in [-0.5, 0.5].\nfn st_centered(uv : vec2f) -> vec2f {\n let aspect = params.resolution.x / max(params.resolution.y, 1.0);\n return vec2f((uv.x - 0.5) * aspect, uv.y - 0.5);\n}\n\nfn st_aspect() -> f32 {\n return params.resolution.x / max(params.resolution.y, 1.0);\n}\n\nfn st_rot(angle : f32) -> mat2x2f {\n let s = sin(angle);\n let c = cos(angle);\n return mat2x2f(c, -s, s, c);\n}\n\n// --- hashing ---------------------------------------------------------------\n\nfn st_hash11(x : f32) -> f32 {\n var p = fract(x * 0.1031);\n p = p * (p + 33.33);\n return fract(p * (p + p));\n}\n\nfn st_hash21(p : vec2f) -> f32 {\n var p3 = fract(vec3f(p.xyx) * 0.1031);\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn st_hash22(p : vec2f) -> vec2f {\n var p3 = fract(vec3f(p.xyx) * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\nfn st_hash33(p : vec3f) -> vec3f {\n var p3 = fract(p * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.xxy + p3.yxx) * p3.zyx);\n}\n\n// --- noise -----------------------------------------------------------------\n\nfn st_noise2(p : vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = st_hash21(i);\n let b = st_hash21(i + vec2f(1.0, 0.0));\n let c = st_hash21(i + vec2f(0.0, 1.0));\n let d = st_hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfn st_noise3(p : vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let n000 = st_hash33(i + vec3f(0.0, 0.0, 0.0)).x;\n let n100 = st_hash33(i + vec3f(1.0, 0.0, 0.0)).x;\n let n010 = st_hash33(i + vec3f(0.0, 1.0, 0.0)).x;\n let n110 = st_hash33(i + vec3f(1.0, 1.0, 0.0)).x;\n let n001 = st_hash33(i + vec3f(0.0, 0.0, 1.0)).x;\n let n101 = st_hash33(i + vec3f(1.0, 0.0, 1.0)).x;\n let n011 = st_hash33(i + vec3f(0.0, 1.0, 1.0)).x;\n let n111 = st_hash33(i + vec3f(1.0, 1.0, 1.0)).x;\n let x00 = mix(n000, n100, u.x);\n let x10 = mix(n010, n110, u.x);\n let x01 = mix(n001, n101, u.x);\n let x11 = mix(n011, n111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\nfn st_fbm2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.5);\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise2(shifted);\n shifted = rotation * shifted * 2.02;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\nfn st_fbm3(p : vec3f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise3(shifted);\n shifted = shifted * 2.03;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// Ridged noise — sharp crests instead of soft hills.\nfn st_ridge2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.7);\n for (var i = 0; i < octaves; i = i + 1) {\n let n = 1.0 - abs(st_noise2(shifted) * 2.0 - 1.0);\n value = value + amplitude * n * n;\n shifted = rotation * shifted * 2.11;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// --- colour ----------------------------------------------------------------\n\n// Three-stop ramp built from the user's colours. t is clamped to [0, 1].\nfn st_palette(t : f32) -> vec3f {\n let x = clamp(t, 0.0, 1.0);\n let low = mix(params.colorA.rgb, params.colorB.rgb, smoothstep(0.0, 0.5, x));\n return mix(low, params.colorC.rgb, smoothstep(0.5, 1.0, x));\n}\n\n// Same ramp, but wrapping — good for cycling and polar shaders.\nfn st_paletteCyclic(t : f32) -> vec3f {\n let x = fract(t);\n let tri = 1.0 - abs(x * 2.0 - 1.0);\n return st_palette(tri);\n}\n\nfn st_luma(c : vec3f) -> f32 {\n return dot(c, vec3f(0.2126, 0.7152, 0.0722));\n}\n\nfn st_saturation(c : vec3f, amount : f32) -> vec3f {\n return mix(vec3f(st_luma(c)), c, amount);\n}\n\nfn st_tonemap(c : vec3f) -> vec3f {\n let x = max(c, vec3f(0.0));\n return (x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14);\n}\n\n// --- shared post chain -----------------------------------------------------\n// Every shader ends here, so vibrancy / grain / vignette behave identically\n// across the whole catalogue.\nfn st_post(color : vec3f, uv : vec2f) -> vec4f {\n var c = st_saturation(max(color, vec3f(0.0)), params.vibrancy);\n\n if (params.grain > 0.0) {\n let seed = uv * params.resolution + vec2f(params.time * 61.7, params.time * 37.3);\n let n = st_hash21(floor(seed));\n c = c + (n - 0.5) * params.grain;\n }\n\n if (params.vignette > 0.0) {\n let d = length(uv - vec2f(0.5)) * 1.41421356;\n c = c * (1.0 - params.vignette * smoothstep(0.22, 1.0, d));\n }\n\n return vec4f(clamp(c, vec3f(0.0), vec3f(1.0)), 1.0);\n}\n\n// ---------------------------------------------------------------------------\n// caustics\n// ---------------------------------------------------------------------------\n\n// Caustics — light focused through a moving water surface onto a pool floor.\n\nfn causticField(p : vec2f, t : f32, sharpness : f32) -> f32 {\n var acc = 0.0;\n var q = p;\n // Stacked, counter-rotating cell fields approximate the focusing pattern.\n for (var i = 0; i < 3; i = i + 1) {\n let fi = f32(i);\n let rotated = st_rot(fi * 1.1 + t * 0.05 * (fi + 1.0)) * q;\n let wave = sin(rotated.x * 6.0 + t * (1.1 + fi * 0.3))\n + sin(rotated.y * 6.0 - t * (0.9 + fi * 0.2))\n + sin((rotated.x + rotated.y) * 4.0 + t * 0.7);\n acc = acc + abs(wave) / 3.0;\n q = q * 1.35;\n }\n let v = 1.0 - clamp(acc / 3.0, 0.0, 1.0);\n return pow(v, 1.0 + sharpness * 14.0);\n}\n\n@fragment fn fs_main(@location(0) uv : vec2f) -> @location(0) vec4f {\n let t = st_time();\n var p = st_centered(uv) * params.scale * 2.0;\n\n // Surface ripple displaces the floor lookup.\n let ripple = vec2f(st_fbm2(p * 1.5 + t * 0.1, 3), st_fbm2(p * 1.5 + 4.2 - t * 0.08, 3));\n p = p + (ripple - 0.5) * params.warp;\n\n let light = causticField(p, t, params.a);\n\n // Chromatic split — the three wavelengths focus at slightly different depths.\n let split = params.b * 0.03;\n let r = causticField(p + vec2f(split, 0.0), t, params.a);\n let b = causticField(p - vec2f(split, 0.0), t, params.a);\n\n let floorTone = mix(params.colorA.rgb, params.colorB.rgb, clamp(st_fbm2(p * 0.8, 3), 0.0, 1.0)) * 0.35;\n var col = floorTone;\n col = col + params.colorC.rgb * vec3f(r, light, b) * params.intensity * 2.2;\n\n // Depth falloff towards the pool edges.\n col = col * (0.55 + 0.45 * smoothstep(1.1, 0.1, length(st_centered(uv)) * params.c));\n\n return st_post(st_tonemap(col), uv);\n}\n",
|
|
8
|
+
"dither": "// shadertown shared prelude — prepended to every shader at build time.\n// One uniform block is the whole ABI: every shader reads the same struct,\n// so the runtime can swap shaders without changing a binding.\n\nstruct Params {\n resolution : vec2f,\n pointer : vec2f,\n time : f32,\n speed : f32,\n scale : f32,\n intensity : f32,\n vibrancy : f32,\n grain : f32,\n vignette : f32,\n warp : f32,\n a : f32,\n b : f32,\n c : f32,\n d : f32,\n colorA : vec4f,\n colorB : vec4f,\n colorC : vec4f,\n}\n\n@group(0) @binding(0) var<uniform> params : Params;\n\nconst PI : f32 = 3.141592653589793;\nconst TAU : f32 = 6.283185307179586;\n\nfn st_time() -> f32 {\n return params.time * params.speed;\n}\n\n// Centred, aspect-corrected coordinates. y stays in [-0.5, 0.5].\nfn st_centered(uv : vec2f) -> vec2f {\n let aspect = params.resolution.x / max(params.resolution.y, 1.0);\n return vec2f((uv.x - 0.5) * aspect, uv.y - 0.5);\n}\n\nfn st_aspect() -> f32 {\n return params.resolution.x / max(params.resolution.y, 1.0);\n}\n\nfn st_rot(angle : f32) -> mat2x2f {\n let s = sin(angle);\n let c = cos(angle);\n return mat2x2f(c, -s, s, c);\n}\n\n// --- hashing ---------------------------------------------------------------\n\nfn st_hash11(x : f32) -> f32 {\n var p = fract(x * 0.1031);\n p = p * (p + 33.33);\n return fract(p * (p + p));\n}\n\nfn st_hash21(p : vec2f) -> f32 {\n var p3 = fract(vec3f(p.xyx) * 0.1031);\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn st_hash22(p : vec2f) -> vec2f {\n var p3 = fract(vec3f(p.xyx) * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\nfn st_hash33(p : vec3f) -> vec3f {\n var p3 = fract(p * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.xxy + p3.yxx) * p3.zyx);\n}\n\n// --- noise -----------------------------------------------------------------\n\nfn st_noise2(p : vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = st_hash21(i);\n let b = st_hash21(i + vec2f(1.0, 0.0));\n let c = st_hash21(i + vec2f(0.0, 1.0));\n let d = st_hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfn st_noise3(p : vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let n000 = st_hash33(i + vec3f(0.0, 0.0, 0.0)).x;\n let n100 = st_hash33(i + vec3f(1.0, 0.0, 0.0)).x;\n let n010 = st_hash33(i + vec3f(0.0, 1.0, 0.0)).x;\n let n110 = st_hash33(i + vec3f(1.0, 1.0, 0.0)).x;\n let n001 = st_hash33(i + vec3f(0.0, 0.0, 1.0)).x;\n let n101 = st_hash33(i + vec3f(1.0, 0.0, 1.0)).x;\n let n011 = st_hash33(i + vec3f(0.0, 1.0, 1.0)).x;\n let n111 = st_hash33(i + vec3f(1.0, 1.0, 1.0)).x;\n let x00 = mix(n000, n100, u.x);\n let x10 = mix(n010, n110, u.x);\n let x01 = mix(n001, n101, u.x);\n let x11 = mix(n011, n111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\nfn st_fbm2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.5);\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise2(shifted);\n shifted = rotation * shifted * 2.02;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\nfn st_fbm3(p : vec3f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise3(shifted);\n shifted = shifted * 2.03;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// Ridged noise — sharp crests instead of soft hills.\nfn st_ridge2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.7);\n for (var i = 0; i < octaves; i = i + 1) {\n let n = 1.0 - abs(st_noise2(shifted) * 2.0 - 1.0);\n value = value + amplitude * n * n;\n shifted = rotation * shifted * 2.11;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// --- colour ----------------------------------------------------------------\n\n// Three-stop ramp built from the user's colours. t is clamped to [0, 1].\nfn st_palette(t : f32) -> vec3f {\n let x = clamp(t, 0.0, 1.0);\n let low = mix(params.colorA.rgb, params.colorB.rgb, smoothstep(0.0, 0.5, x));\n return mix(low, params.colorC.rgb, smoothstep(0.5, 1.0, x));\n}\n\n// Same ramp, but wrapping — good for cycling and polar shaders.\nfn st_paletteCyclic(t : f32) -> vec3f {\n let x = fract(t);\n let tri = 1.0 - abs(x * 2.0 - 1.0);\n return st_palette(tri);\n}\n\nfn st_luma(c : vec3f) -> f32 {\n return dot(c, vec3f(0.2126, 0.7152, 0.0722));\n}\n\nfn st_saturation(c : vec3f, amount : f32) -> vec3f {\n return mix(vec3f(st_luma(c)), c, amount);\n}\n\nfn st_tonemap(c : vec3f) -> vec3f {\n let x = max(c, vec3f(0.0));\n return (x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14);\n}\n\n// --- shared post chain -----------------------------------------------------\n// Every shader ends here, so vibrancy / grain / vignette behave identically\n// across the whole catalogue.\nfn st_post(color : vec3f, uv : vec2f) -> vec4f {\n var c = st_saturation(max(color, vec3f(0.0)), params.vibrancy);\n\n if (params.grain > 0.0) {\n let seed = uv * params.resolution + vec2f(params.time * 61.7, params.time * 37.3);\n let n = st_hash21(floor(seed));\n c = c + (n - 0.5) * params.grain;\n }\n\n if (params.vignette > 0.0) {\n let d = length(uv - vec2f(0.5)) * 1.41421356;\n c = c * (1.0 - params.vignette * smoothstep(0.22, 1.0, d));\n }\n\n return vec4f(clamp(c, vec3f(0.0), vec3f(1.0)), 1.0);\n}\n\n// ---------------------------------------------------------------------------\n// dither\n// ---------------------------------------------------------------------------\n\n// Dither — a smooth field quantised through an 8x8 Bayer matrix.\n// Deliberately low-fi: flat colour, a visible pattern, no antialiasing.\n\n// Ordered-dither threshold, built by interleaving the bits of y and x^y.\nfn bayer8(coord : vec2f) -> f32 {\n let x = i32(coord.x) & 7;\n let y = i32(coord.y) & 7;\n var index = 0;\n for (var i = 0u; i < 3u; i = i + 1u) {\n let shift = 2u - i;\n index = (index << 1u) | ((y >> shift) & 1);\n index = (index << 1u) | (((x ^ y) >> shift) & 1);\n }\n return f32(index) / 64.0;\n}\n\n@fragment fn fs_main(@location(0) uv : vec2f) -> @location(0) vec4f {\n let t = st_time();\n let p = st_centered(uv) * params.scale;\n\n // Centred on 0.5 so every quantisation level gets used. Biasing the source\n // upwards instead just crushes the image to the top level.\n let field = st_fbm2(p * 2.0 + vec2f(t * 0.10, t * 0.06), 5);\n let sweep = clamp((field - 0.5) * 1.7 + (0.5 - uv.y) * params.c + 0.5, 0.0, 1.0);\n\n // Pixelate first, so the pattern reads the same at any resolution.\n let pixel = max(params.a, 1.0);\n let screenPos = floor(uv * params.resolution / pixel);\n let threshold = (bayer8(screenPos) - 0.5) / max(floor(params.b), 2.0);\n\n let levels = max(floor(params.b), 2.0);\n let stepped = min(floor(clamp(sweep + threshold * params.d * 2.0, 0.0, 1.0) * levels), levels - 1.0);\n let quantised = stepped / (levels - 1.0);\n\n let col = st_palette(quantised) * params.intensity;\n return st_post(col, uv);\n}\n",
|
|
9
|
+
"film-grain": "// shadertown shared prelude — prepended to every shader at build time.\n// One uniform block is the whole ABI: every shader reads the same struct,\n// so the runtime can swap shaders without changing a binding.\n\nstruct Params {\n resolution : vec2f,\n pointer : vec2f,\n time : f32,\n speed : f32,\n scale : f32,\n intensity : f32,\n vibrancy : f32,\n grain : f32,\n vignette : f32,\n warp : f32,\n a : f32,\n b : f32,\n c : f32,\n d : f32,\n colorA : vec4f,\n colorB : vec4f,\n colorC : vec4f,\n}\n\n@group(0) @binding(0) var<uniform> params : Params;\n\nconst PI : f32 = 3.141592653589793;\nconst TAU : f32 = 6.283185307179586;\n\nfn st_time() -> f32 {\n return params.time * params.speed;\n}\n\n// Centred, aspect-corrected coordinates. y stays in [-0.5, 0.5].\nfn st_centered(uv : vec2f) -> vec2f {\n let aspect = params.resolution.x / max(params.resolution.y, 1.0);\n return vec2f((uv.x - 0.5) * aspect, uv.y - 0.5);\n}\n\nfn st_aspect() -> f32 {\n return params.resolution.x / max(params.resolution.y, 1.0);\n}\n\nfn st_rot(angle : f32) -> mat2x2f {\n let s = sin(angle);\n let c = cos(angle);\n return mat2x2f(c, -s, s, c);\n}\n\n// --- hashing ---------------------------------------------------------------\n\nfn st_hash11(x : f32) -> f32 {\n var p = fract(x * 0.1031);\n p = p * (p + 33.33);\n return fract(p * (p + p));\n}\n\nfn st_hash21(p : vec2f) -> f32 {\n var p3 = fract(vec3f(p.xyx) * 0.1031);\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn st_hash22(p : vec2f) -> vec2f {\n var p3 = fract(vec3f(p.xyx) * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\nfn st_hash33(p : vec3f) -> vec3f {\n var p3 = fract(p * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.xxy + p3.yxx) * p3.zyx);\n}\n\n// --- noise -----------------------------------------------------------------\n\nfn st_noise2(p : vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = st_hash21(i);\n let b = st_hash21(i + vec2f(1.0, 0.0));\n let c = st_hash21(i + vec2f(0.0, 1.0));\n let d = st_hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfn st_noise3(p : vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let n000 = st_hash33(i + vec3f(0.0, 0.0, 0.0)).x;\n let n100 = st_hash33(i + vec3f(1.0, 0.0, 0.0)).x;\n let n010 = st_hash33(i + vec3f(0.0, 1.0, 0.0)).x;\n let n110 = st_hash33(i + vec3f(1.0, 1.0, 0.0)).x;\n let n001 = st_hash33(i + vec3f(0.0, 0.0, 1.0)).x;\n let n101 = st_hash33(i + vec3f(1.0, 0.0, 1.0)).x;\n let n011 = st_hash33(i + vec3f(0.0, 1.0, 1.0)).x;\n let n111 = st_hash33(i + vec3f(1.0, 1.0, 1.0)).x;\n let x00 = mix(n000, n100, u.x);\n let x10 = mix(n010, n110, u.x);\n let x01 = mix(n001, n101, u.x);\n let x11 = mix(n011, n111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\nfn st_fbm2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.5);\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise2(shifted);\n shifted = rotation * shifted * 2.02;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\nfn st_fbm3(p : vec3f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise3(shifted);\n shifted = shifted * 2.03;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// Ridged noise — sharp crests instead of soft hills.\nfn st_ridge2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.7);\n for (var i = 0; i < octaves; i = i + 1) {\n let n = 1.0 - abs(st_noise2(shifted) * 2.0 - 1.0);\n value = value + amplitude * n * n;\n shifted = rotation * shifted * 2.11;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// --- colour ----------------------------------------------------------------\n\n// Three-stop ramp built from the user's colours. t is clamped to [0, 1].\nfn st_palette(t : f32) -> vec3f {\n let x = clamp(t, 0.0, 1.0);\n let low = mix(params.colorA.rgb, params.colorB.rgb, smoothstep(0.0, 0.5, x));\n return mix(low, params.colorC.rgb, smoothstep(0.5, 1.0, x));\n}\n\n// Same ramp, but wrapping — good for cycling and polar shaders.\nfn st_paletteCyclic(t : f32) -> vec3f {\n let x = fract(t);\n let tri = 1.0 - abs(x * 2.0 - 1.0);\n return st_palette(tri);\n}\n\nfn st_luma(c : vec3f) -> f32 {\n return dot(c, vec3f(0.2126, 0.7152, 0.0722));\n}\n\nfn st_saturation(c : vec3f, amount : f32) -> vec3f {\n return mix(vec3f(st_luma(c)), c, amount);\n}\n\nfn st_tonemap(c : vec3f) -> vec3f {\n let x = max(c, vec3f(0.0));\n return (x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14);\n}\n\n// --- shared post chain -----------------------------------------------------\n// Every shader ends here, so vibrancy / grain / vignette behave identically\n// across the whole catalogue.\nfn st_post(color : vec3f, uv : vec2f) -> vec4f {\n var c = st_saturation(max(color, vec3f(0.0)), params.vibrancy);\n\n if (params.grain > 0.0) {\n let seed = uv * params.resolution + vec2f(params.time * 61.7, params.time * 37.3);\n let n = st_hash21(floor(seed));\n c = c + (n - 0.5) * params.grain;\n }\n\n if (params.vignette > 0.0) {\n let d = length(uv - vec2f(0.5)) * 1.41421356;\n c = c * (1.0 - params.vignette * smoothstep(0.22, 1.0, d));\n }\n\n return vec4f(clamp(c, vec3f(0.0), vec3f(1.0)), 1.0);\n}\n\n// ---------------------------------------------------------------------------\n// film-grain\n// ---------------------------------------------------------------------------\n\n// Film grain — a texture pass. Dark wash, heavy stock grain, halation, scanlines.\n\nfn wash(p : vec2f, t : f32) -> f32 {\n return st_fbm2(p * 1.2 + vec2f(t * 0.05, t * 0.03), 4);\n}\n\n@fragment fn fs_main(@location(0) uv : vec2f) -> @location(0) vec4f {\n let t = st_time();\n let p = st_centered(uv) * params.scale;\n\n // Chromatic aberration: read the same wash at three lateral offsets.\n let ca = params.c * 0.02;\n let wr = wash(p + vec2f(ca, 0.0), t);\n let wg = wash(p, t);\n let wb = wash(p - vec2f(ca, 0.0), t);\n\n var col = vec3f(\n st_palette(clamp(wr * 1.3 - 0.1, 0.0, 1.0)).r,\n st_palette(clamp(wg * 1.3 - 0.1, 0.0, 1.0)).g,\n st_palette(clamp(wb * 1.3 - 0.1, 0.0, 1.0)).b\n ) * params.intensity;\n\n // Stock grain, sampled per cell so it stays the same size at any DPR.\n let cell = floor(uv * params.resolution / max(params.d, 0.5));\n let n1 = st_hash21(cell + floor(t * 24.0) * 17.13);\n let n2 = st_hash21(cell.yx + floor(t * 24.0) * 3.71);\n col = col + (n1 * 0.65 + n2 * 0.35 - 0.5) * params.a;\n\n // Scanlines.\n let lines = sin(uv.y * params.resolution.y * 1.5) * 0.5 + 0.5;\n col = col * (1.0 - lines * params.b);\n\n // Halation blooming out of the brightest areas.\n col = col + st_palette(1.0) * pow(clamp(st_luma(col), 0.0, 1.0), 4.0) * 0.4;\n\n return st_post(col, uv);\n}\n",
|
|
10
|
+
"halftone": "// shadertown shared prelude — prepended to every shader at build time.\n// One uniform block is the whole ABI: every shader reads the same struct,\n// so the runtime can swap shaders without changing a binding.\n\nstruct Params {\n resolution : vec2f,\n pointer : vec2f,\n time : f32,\n speed : f32,\n scale : f32,\n intensity : f32,\n vibrancy : f32,\n grain : f32,\n vignette : f32,\n warp : f32,\n a : f32,\n b : f32,\n c : f32,\n d : f32,\n colorA : vec4f,\n colorB : vec4f,\n colorC : vec4f,\n}\n\n@group(0) @binding(0) var<uniform> params : Params;\n\nconst PI : f32 = 3.141592653589793;\nconst TAU : f32 = 6.283185307179586;\n\nfn st_time() -> f32 {\n return params.time * params.speed;\n}\n\n// Centred, aspect-corrected coordinates. y stays in [-0.5, 0.5].\nfn st_centered(uv : vec2f) -> vec2f {\n let aspect = params.resolution.x / max(params.resolution.y, 1.0);\n return vec2f((uv.x - 0.5) * aspect, uv.y - 0.5);\n}\n\nfn st_aspect() -> f32 {\n return params.resolution.x / max(params.resolution.y, 1.0);\n}\n\nfn st_rot(angle : f32) -> mat2x2f {\n let s = sin(angle);\n let c = cos(angle);\n return mat2x2f(c, -s, s, c);\n}\n\n// --- hashing ---------------------------------------------------------------\n\nfn st_hash11(x : f32) -> f32 {\n var p = fract(x * 0.1031);\n p = p * (p + 33.33);\n return fract(p * (p + p));\n}\n\nfn st_hash21(p : vec2f) -> f32 {\n var p3 = fract(vec3f(p.xyx) * 0.1031);\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn st_hash22(p : vec2f) -> vec2f {\n var p3 = fract(vec3f(p.xyx) * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\nfn st_hash33(p : vec3f) -> vec3f {\n var p3 = fract(p * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.xxy + p3.yxx) * p3.zyx);\n}\n\n// --- noise -----------------------------------------------------------------\n\nfn st_noise2(p : vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = st_hash21(i);\n let b = st_hash21(i + vec2f(1.0, 0.0));\n let c = st_hash21(i + vec2f(0.0, 1.0));\n let d = st_hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfn st_noise3(p : vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let n000 = st_hash33(i + vec3f(0.0, 0.0, 0.0)).x;\n let n100 = st_hash33(i + vec3f(1.0, 0.0, 0.0)).x;\n let n010 = st_hash33(i + vec3f(0.0, 1.0, 0.0)).x;\n let n110 = st_hash33(i + vec3f(1.0, 1.0, 0.0)).x;\n let n001 = st_hash33(i + vec3f(0.0, 0.0, 1.0)).x;\n let n101 = st_hash33(i + vec3f(1.0, 0.0, 1.0)).x;\n let n011 = st_hash33(i + vec3f(0.0, 1.0, 1.0)).x;\n let n111 = st_hash33(i + vec3f(1.0, 1.0, 1.0)).x;\n let x00 = mix(n000, n100, u.x);\n let x10 = mix(n010, n110, u.x);\n let x01 = mix(n001, n101, u.x);\n let x11 = mix(n011, n111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\nfn st_fbm2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.5);\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise2(shifted);\n shifted = rotation * shifted * 2.02;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\nfn st_fbm3(p : vec3f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise3(shifted);\n shifted = shifted * 2.03;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// Ridged noise — sharp crests instead of soft hills.\nfn st_ridge2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.7);\n for (var i = 0; i < octaves; i = i + 1) {\n let n = 1.0 - abs(st_noise2(shifted) * 2.0 - 1.0);\n value = value + amplitude * n * n;\n shifted = rotation * shifted * 2.11;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// --- colour ----------------------------------------------------------------\n\n// Three-stop ramp built from the user's colours. t is clamped to [0, 1].\nfn st_palette(t : f32) -> vec3f {\n let x = clamp(t, 0.0, 1.0);\n let low = mix(params.colorA.rgb, params.colorB.rgb, smoothstep(0.0, 0.5, x));\n return mix(low, params.colorC.rgb, smoothstep(0.5, 1.0, x));\n}\n\n// Same ramp, but wrapping — good for cycling and polar shaders.\nfn st_paletteCyclic(t : f32) -> vec3f {\n let x = fract(t);\n let tri = 1.0 - abs(x * 2.0 - 1.0);\n return st_palette(tri);\n}\n\nfn st_luma(c : vec3f) -> f32 {\n return dot(c, vec3f(0.2126, 0.7152, 0.0722));\n}\n\nfn st_saturation(c : vec3f, amount : f32) -> vec3f {\n return mix(vec3f(st_luma(c)), c, amount);\n}\n\nfn st_tonemap(c : vec3f) -> vec3f {\n let x = max(c, vec3f(0.0));\n return (x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14);\n}\n\n// --- shared post chain -----------------------------------------------------\n// Every shader ends here, so vibrancy / grain / vignette behave identically\n// across the whole catalogue.\nfn st_post(color : vec3f, uv : vec2f) -> vec4f {\n var c = st_saturation(max(color, vec3f(0.0)), params.vibrancy);\n\n if (params.grain > 0.0) {\n let seed = uv * params.resolution + vec2f(params.time * 61.7, params.time * 37.3);\n let n = st_hash21(floor(seed));\n c = c + (n - 0.5) * params.grain;\n }\n\n if (params.vignette > 0.0) {\n let d = length(uv - vec2f(0.5)) * 1.41421356;\n c = c * (1.0 - params.vignette * smoothstep(0.22, 1.0, d));\n }\n\n return vec4f(clamp(c, vec3f(0.0), vec3f(1.0)), 1.0);\n}\n\n// ---------------------------------------------------------------------------\n// halftone\n// ---------------------------------------------------------------------------\n\n// Halftone — a duotone image reproduced as a rotated dot screen.\n\n@fragment fn fs_main(@location(0) uv : vec2f) -> @location(0) vec4f {\n let t = st_time();\n let p = st_centered(uv) * params.scale;\n\n // The continuous-tone image being screened. Centred on 0.5 so the screen\n // actually has a range of dot sizes to work with instead of saturating.\n let field = st_fbm2(p * 1.8 + vec2f(t * 0.12, t * 0.07), 4);\n let sweep = clamp((field - 0.5) * 1.6 + (p.x + p.y) * 0.35 + 0.5, 0.0, 1.0);\n\n // Dot screen, rotated the way a real halftone plate would be.\n let angle = params.c * PI;\n let grid = st_rot(angle) * uv * params.resolution / max(params.a, 2.0);\n let cellPos = fract(grid) - 0.5;\n\n let ink = pow(sweep, 1.0 / max(params.d, 0.05));\n // 0.62 lets neighbouring dots touch at full ink while leaving the corners\n // open — past ~0.71 the screen fills solid and stops reading as halftone.\n let radius = sqrt(ink) * 0.62;\n let coverage = 1.0 - smoothstep(radius - params.b, radius + params.b, length(cellPos));\n\n let paper = params.colorA.rgb;\n let inkColor = mix(params.colorB.rgb, params.colorC.rgb, sweep);\n\n let col = mix(paper, inkColor, coverage * params.intensity);\n return st_post(col, uv);\n}\n",
|
|
11
|
+
"julia": "// shadertown shared prelude — prepended to every shader at build time.\n// One uniform block is the whole ABI: every shader reads the same struct,\n// so the runtime can swap shaders without changing a binding.\n\nstruct Params {\n resolution : vec2f,\n pointer : vec2f,\n time : f32,\n speed : f32,\n scale : f32,\n intensity : f32,\n vibrancy : f32,\n grain : f32,\n vignette : f32,\n warp : f32,\n a : f32,\n b : f32,\n c : f32,\n d : f32,\n colorA : vec4f,\n colorB : vec4f,\n colorC : vec4f,\n}\n\n@group(0) @binding(0) var<uniform> params : Params;\n\nconst PI : f32 = 3.141592653589793;\nconst TAU : f32 = 6.283185307179586;\n\nfn st_time() -> f32 {\n return params.time * params.speed;\n}\n\n// Centred, aspect-corrected coordinates. y stays in [-0.5, 0.5].\nfn st_centered(uv : vec2f) -> vec2f {\n let aspect = params.resolution.x / max(params.resolution.y, 1.0);\n return vec2f((uv.x - 0.5) * aspect, uv.y - 0.5);\n}\n\nfn st_aspect() -> f32 {\n return params.resolution.x / max(params.resolution.y, 1.0);\n}\n\nfn st_rot(angle : f32) -> mat2x2f {\n let s = sin(angle);\n let c = cos(angle);\n return mat2x2f(c, -s, s, c);\n}\n\n// --- hashing ---------------------------------------------------------------\n\nfn st_hash11(x : f32) -> f32 {\n var p = fract(x * 0.1031);\n p = p * (p + 33.33);\n return fract(p * (p + p));\n}\n\nfn st_hash21(p : vec2f) -> f32 {\n var p3 = fract(vec3f(p.xyx) * 0.1031);\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn st_hash22(p : vec2f) -> vec2f {\n var p3 = fract(vec3f(p.xyx) * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\nfn st_hash33(p : vec3f) -> vec3f {\n var p3 = fract(p * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.xxy + p3.yxx) * p3.zyx);\n}\n\n// --- noise -----------------------------------------------------------------\n\nfn st_noise2(p : vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = st_hash21(i);\n let b = st_hash21(i + vec2f(1.0, 0.0));\n let c = st_hash21(i + vec2f(0.0, 1.0));\n let d = st_hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfn st_noise3(p : vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let n000 = st_hash33(i + vec3f(0.0, 0.0, 0.0)).x;\n let n100 = st_hash33(i + vec3f(1.0, 0.0, 0.0)).x;\n let n010 = st_hash33(i + vec3f(0.0, 1.0, 0.0)).x;\n let n110 = st_hash33(i + vec3f(1.0, 1.0, 0.0)).x;\n let n001 = st_hash33(i + vec3f(0.0, 0.0, 1.0)).x;\n let n101 = st_hash33(i + vec3f(1.0, 0.0, 1.0)).x;\n let n011 = st_hash33(i + vec3f(0.0, 1.0, 1.0)).x;\n let n111 = st_hash33(i + vec3f(1.0, 1.0, 1.0)).x;\n let x00 = mix(n000, n100, u.x);\n let x10 = mix(n010, n110, u.x);\n let x01 = mix(n001, n101, u.x);\n let x11 = mix(n011, n111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\nfn st_fbm2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.5);\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise2(shifted);\n shifted = rotation * shifted * 2.02;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\nfn st_fbm3(p : vec3f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise3(shifted);\n shifted = shifted * 2.03;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// Ridged noise — sharp crests instead of soft hills.\nfn st_ridge2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.7);\n for (var i = 0; i < octaves; i = i + 1) {\n let n = 1.0 - abs(st_noise2(shifted) * 2.0 - 1.0);\n value = value + amplitude * n * n;\n shifted = rotation * shifted * 2.11;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// --- colour ----------------------------------------------------------------\n\n// Three-stop ramp built from the user's colours. t is clamped to [0, 1].\nfn st_palette(t : f32) -> vec3f {\n let x = clamp(t, 0.0, 1.0);\n let low = mix(params.colorA.rgb, params.colorB.rgb, smoothstep(0.0, 0.5, x));\n return mix(low, params.colorC.rgb, smoothstep(0.5, 1.0, x));\n}\n\n// Same ramp, but wrapping — good for cycling and polar shaders.\nfn st_paletteCyclic(t : f32) -> vec3f {\n let x = fract(t);\n let tri = 1.0 - abs(x * 2.0 - 1.0);\n return st_palette(tri);\n}\n\nfn st_luma(c : vec3f) -> f32 {\n return dot(c, vec3f(0.2126, 0.7152, 0.0722));\n}\n\nfn st_saturation(c : vec3f, amount : f32) -> vec3f {\n return mix(vec3f(st_luma(c)), c, amount);\n}\n\nfn st_tonemap(c : vec3f) -> vec3f {\n let x = max(c, vec3f(0.0));\n return (x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14);\n}\n\n// --- shared post chain -----------------------------------------------------\n// Every shader ends here, so vibrancy / grain / vignette behave identically\n// across the whole catalogue.\nfn st_post(color : vec3f, uv : vec2f) -> vec4f {\n var c = st_saturation(max(color, vec3f(0.0)), params.vibrancy);\n\n if (params.grain > 0.0) {\n let seed = uv * params.resolution + vec2f(params.time * 61.7, params.time * 37.3);\n let n = st_hash21(floor(seed));\n c = c + (n - 0.5) * params.grain;\n }\n\n if (params.vignette > 0.0) {\n let d = length(uv - vec2f(0.5)) * 1.41421356;\n c = c * (1.0 - params.vignette * smoothstep(0.22, 1.0, d));\n }\n\n return vec4f(clamp(c, vec3f(0.0), vec3f(1.0)), 1.0);\n}\n\n// ---------------------------------------------------------------------------\n// julia\n// ---------------------------------------------------------------------------\n\n// Julia — escape-time fractal with orbit-trap colouring. A moving c parameter\n// walks the set through connected and dust-like shapes.\n\n@fragment fn fs_main(@location(0) uv : vec2f) -> @location(0) vec4f {\n let t = st_time();\n var z = st_centered(uv) * (3.4 / max(params.scale, 0.05));\n z = st_rot(t * 0.03 * params.d) * z;\n\n // The seed orbits a cardioid edge, which is where the interesting shapes live.\n let angle = t * 0.12;\n let c = vec2f(\n 0.7885 * cos(angle) * params.a,\n 0.7885 * sin(angle) * params.a\n ) + vec2f(params.b - 0.5, 0.0) * 0.6;\n\n let maxIter = 96;\n var i = 0;\n var trap = 1e9;\n var escaped = false;\n\n for (; i < maxIter; i = i + 1) {\n z = vec2f(z.x * z.x - z.y * z.y, 2.0 * z.x * z.y) + c;\n trap = min(trap, abs(length(z) - 1.0));\n if (dot(z, z) > 64.0) { escaped = true; break; }\n }\n\n if (!escaped) {\n // Interior: shade by how close the orbit came to the trap circle.\n let inner = st_palette(clamp(trap * 4.0, 0.0, 1.0)) * 0.5;\n return st_post(inner * params.intensity, uv);\n }\n\n // Smooth iteration count removes the banding of a raw integer count.\n let smoothed = f32(i) - log2(max(log2(length(z)), 1.0)) + 4.0;\n let shade = fract(smoothed * 0.09 * params.c + t * 0.04);\n\n // Points that escape immediately are the empty outer plane; letting them\n // take a full palette colour washes the whole frame out.\n let escapeDepth = clamp(smoothed / 18.0, 0.0, 1.0);\n\n var col = st_paletteCyclic(shade) * pow(escapeDepth, 1.15);\n col = col * (0.25 + 0.9 * clamp(1.0 - trap * 1.1, 0.0, 1.0));\n\n return st_post(col * params.intensity, uv);\n}\n",
|
|
12
|
+
"kaleidoscope": "// shadertown shared prelude — prepended to every shader at build time.\n// One uniform block is the whole ABI: every shader reads the same struct,\n// so the runtime can swap shaders without changing a binding.\n\nstruct Params {\n resolution : vec2f,\n pointer : vec2f,\n time : f32,\n speed : f32,\n scale : f32,\n intensity : f32,\n vibrancy : f32,\n grain : f32,\n vignette : f32,\n warp : f32,\n a : f32,\n b : f32,\n c : f32,\n d : f32,\n colorA : vec4f,\n colorB : vec4f,\n colorC : vec4f,\n}\n\n@group(0) @binding(0) var<uniform> params : Params;\n\nconst PI : f32 = 3.141592653589793;\nconst TAU : f32 = 6.283185307179586;\n\nfn st_time() -> f32 {\n return params.time * params.speed;\n}\n\n// Centred, aspect-corrected coordinates. y stays in [-0.5, 0.5].\nfn st_centered(uv : vec2f) -> vec2f {\n let aspect = params.resolution.x / max(params.resolution.y, 1.0);\n return vec2f((uv.x - 0.5) * aspect, uv.y - 0.5);\n}\n\nfn st_aspect() -> f32 {\n return params.resolution.x / max(params.resolution.y, 1.0);\n}\n\nfn st_rot(angle : f32) -> mat2x2f {\n let s = sin(angle);\n let c = cos(angle);\n return mat2x2f(c, -s, s, c);\n}\n\n// --- hashing ---------------------------------------------------------------\n\nfn st_hash11(x : f32) -> f32 {\n var p = fract(x * 0.1031);\n p = p * (p + 33.33);\n return fract(p * (p + p));\n}\n\nfn st_hash21(p : vec2f) -> f32 {\n var p3 = fract(vec3f(p.xyx) * 0.1031);\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn st_hash22(p : vec2f) -> vec2f {\n var p3 = fract(vec3f(p.xyx) * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\nfn st_hash33(p : vec3f) -> vec3f {\n var p3 = fract(p * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.xxy + p3.yxx) * p3.zyx);\n}\n\n// --- noise -----------------------------------------------------------------\n\nfn st_noise2(p : vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = st_hash21(i);\n let b = st_hash21(i + vec2f(1.0, 0.0));\n let c = st_hash21(i + vec2f(0.0, 1.0));\n let d = st_hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfn st_noise3(p : vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let n000 = st_hash33(i + vec3f(0.0, 0.0, 0.0)).x;\n let n100 = st_hash33(i + vec3f(1.0, 0.0, 0.0)).x;\n let n010 = st_hash33(i + vec3f(0.0, 1.0, 0.0)).x;\n let n110 = st_hash33(i + vec3f(1.0, 1.0, 0.0)).x;\n let n001 = st_hash33(i + vec3f(0.0, 0.0, 1.0)).x;\n let n101 = st_hash33(i + vec3f(1.0, 0.0, 1.0)).x;\n let n011 = st_hash33(i + vec3f(0.0, 1.0, 1.0)).x;\n let n111 = st_hash33(i + vec3f(1.0, 1.0, 1.0)).x;\n let x00 = mix(n000, n100, u.x);\n let x10 = mix(n010, n110, u.x);\n let x01 = mix(n001, n101, u.x);\n let x11 = mix(n011, n111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\nfn st_fbm2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.5);\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise2(shifted);\n shifted = rotation * shifted * 2.02;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\nfn st_fbm3(p : vec3f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise3(shifted);\n shifted = shifted * 2.03;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// Ridged noise — sharp crests instead of soft hills.\nfn st_ridge2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.7);\n for (var i = 0; i < octaves; i = i + 1) {\n let n = 1.0 - abs(st_noise2(shifted) * 2.0 - 1.0);\n value = value + amplitude * n * n;\n shifted = rotation * shifted * 2.11;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// --- colour ----------------------------------------------------------------\n\n// Three-stop ramp built from the user's colours. t is clamped to [0, 1].\nfn st_palette(t : f32) -> vec3f {\n let x = clamp(t, 0.0, 1.0);\n let low = mix(params.colorA.rgb, params.colorB.rgb, smoothstep(0.0, 0.5, x));\n return mix(low, params.colorC.rgb, smoothstep(0.5, 1.0, x));\n}\n\n// Same ramp, but wrapping — good for cycling and polar shaders.\nfn st_paletteCyclic(t : f32) -> vec3f {\n let x = fract(t);\n let tri = 1.0 - abs(x * 2.0 - 1.0);\n return st_palette(tri);\n}\n\nfn st_luma(c : vec3f) -> f32 {\n return dot(c, vec3f(0.2126, 0.7152, 0.0722));\n}\n\nfn st_saturation(c : vec3f, amount : f32) -> vec3f {\n return mix(vec3f(st_luma(c)), c, amount);\n}\n\nfn st_tonemap(c : vec3f) -> vec3f {\n let x = max(c, vec3f(0.0));\n return (x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14);\n}\n\n// --- shared post chain -----------------------------------------------------\n// Every shader ends here, so vibrancy / grain / vignette behave identically\n// across the whole catalogue.\nfn st_post(color : vec3f, uv : vec2f) -> vec4f {\n var c = st_saturation(max(color, vec3f(0.0)), params.vibrancy);\n\n if (params.grain > 0.0) {\n let seed = uv * params.resolution + vec2f(params.time * 61.7, params.time * 37.3);\n let n = st_hash21(floor(seed));\n c = c + (n - 0.5) * params.grain;\n }\n\n if (params.vignette > 0.0) {\n let d = length(uv - vec2f(0.5)) * 1.41421356;\n c = c * (1.0 - params.vignette * smoothstep(0.22, 1.0, d));\n }\n\n return vec4f(clamp(c, vec3f(0.0), vec3f(1.0)), 1.0);\n}\n\n// ---------------------------------------------------------------------------\n// kaleidoscope\n// ---------------------------------------------------------------------------\n\n// Kaleidoscope — polar mirror folding over a drifting noise field.\n\n@fragment fn fs_main(@location(0) uv : vec2f) -> @location(0) vec4f {\n var p = st_centered(uv) * params.scale;\n let t = st_time();\n\n let segments = max(floor(params.a), 2.0);\n var angle = atan2(p.y, p.x);\n let radius = length(p);\n\n // Fold the angle into one wedge, then mirror it.\n let wedge = TAU / segments;\n angle = angle + t * 0.15 * params.d;\n angle = angle - wedge * floor(angle / wedge);\n angle = abs(angle - wedge * 0.5);\n\n var q = vec2f(cos(angle), sin(angle)) * radius;\n q = q + vec2f(st_fbm2(q * 3.0 + t * 0.2, 4) - 0.5) * params.warp;\n\n let rings = sin(radius * 22.0 * params.b - t * 2.0) * 0.5 + 0.5;\n let field = st_fbm2(q * 4.0 + vec2f(0.0, t * 0.3), 5);\n\n var col = st_palette(fract(field * 1.6 + rings * params.c));\n\n // Bright core, dark rim — reads like looking down a tube.\n col = col * (0.4 + 0.9 * smoothstep(0.75, 0.0, radius));\n col = col + st_palette(1.0) * pow(clamp(1.0 - radius * 2.2, 0.0, 1.0), 3.0) * 0.6;\n\n return st_post(col * params.intensity, uv);\n}\n",
|
|
13
|
+
"liquid-metal": "// shadertown shared prelude — prepended to every shader at build time.\n// One uniform block is the whole ABI: every shader reads the same struct,\n// so the runtime can swap shaders without changing a binding.\n\nstruct Params {\n resolution : vec2f,\n pointer : vec2f,\n time : f32,\n speed : f32,\n scale : f32,\n intensity : f32,\n vibrancy : f32,\n grain : f32,\n vignette : f32,\n warp : f32,\n a : f32,\n b : f32,\n c : f32,\n d : f32,\n colorA : vec4f,\n colorB : vec4f,\n colorC : vec4f,\n}\n\n@group(0) @binding(0) var<uniform> params : Params;\n\nconst PI : f32 = 3.141592653589793;\nconst TAU : f32 = 6.283185307179586;\n\nfn st_time() -> f32 {\n return params.time * params.speed;\n}\n\n// Centred, aspect-corrected coordinates. y stays in [-0.5, 0.5].\nfn st_centered(uv : vec2f) -> vec2f {\n let aspect = params.resolution.x / max(params.resolution.y, 1.0);\n return vec2f((uv.x - 0.5) * aspect, uv.y - 0.5);\n}\n\nfn st_aspect() -> f32 {\n return params.resolution.x / max(params.resolution.y, 1.0);\n}\n\nfn st_rot(angle : f32) -> mat2x2f {\n let s = sin(angle);\n let c = cos(angle);\n return mat2x2f(c, -s, s, c);\n}\n\n// --- hashing ---------------------------------------------------------------\n\nfn st_hash11(x : f32) -> f32 {\n var p = fract(x * 0.1031);\n p = p * (p + 33.33);\n return fract(p * (p + p));\n}\n\nfn st_hash21(p : vec2f) -> f32 {\n var p3 = fract(vec3f(p.xyx) * 0.1031);\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn st_hash22(p : vec2f) -> vec2f {\n var p3 = fract(vec3f(p.xyx) * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\nfn st_hash33(p : vec3f) -> vec3f {\n var p3 = fract(p * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.xxy + p3.yxx) * p3.zyx);\n}\n\n// --- noise -----------------------------------------------------------------\n\nfn st_noise2(p : vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = st_hash21(i);\n let b = st_hash21(i + vec2f(1.0, 0.0));\n let c = st_hash21(i + vec2f(0.0, 1.0));\n let d = st_hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfn st_noise3(p : vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let n000 = st_hash33(i + vec3f(0.0, 0.0, 0.0)).x;\n let n100 = st_hash33(i + vec3f(1.0, 0.0, 0.0)).x;\n let n010 = st_hash33(i + vec3f(0.0, 1.0, 0.0)).x;\n let n110 = st_hash33(i + vec3f(1.0, 1.0, 0.0)).x;\n let n001 = st_hash33(i + vec3f(0.0, 0.0, 1.0)).x;\n let n101 = st_hash33(i + vec3f(1.0, 0.0, 1.0)).x;\n let n011 = st_hash33(i + vec3f(0.0, 1.0, 1.0)).x;\n let n111 = st_hash33(i + vec3f(1.0, 1.0, 1.0)).x;\n let x00 = mix(n000, n100, u.x);\n let x10 = mix(n010, n110, u.x);\n let x01 = mix(n001, n101, u.x);\n let x11 = mix(n011, n111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\nfn st_fbm2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.5);\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise2(shifted);\n shifted = rotation * shifted * 2.02;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\nfn st_fbm3(p : vec3f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise3(shifted);\n shifted = shifted * 2.03;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// Ridged noise — sharp crests instead of soft hills.\nfn st_ridge2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.7);\n for (var i = 0; i < octaves; i = i + 1) {\n let n = 1.0 - abs(st_noise2(shifted) * 2.0 - 1.0);\n value = value + amplitude * n * n;\n shifted = rotation * shifted * 2.11;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// --- colour ----------------------------------------------------------------\n\n// Three-stop ramp built from the user's colours. t is clamped to [0, 1].\nfn st_palette(t : f32) -> vec3f {\n let x = clamp(t, 0.0, 1.0);\n let low = mix(params.colorA.rgb, params.colorB.rgb, smoothstep(0.0, 0.5, x));\n return mix(low, params.colorC.rgb, smoothstep(0.5, 1.0, x));\n}\n\n// Same ramp, but wrapping — good for cycling and polar shaders.\nfn st_paletteCyclic(t : f32) -> vec3f {\n let x = fract(t);\n let tri = 1.0 - abs(x * 2.0 - 1.0);\n return st_palette(tri);\n}\n\nfn st_luma(c : vec3f) -> f32 {\n return dot(c, vec3f(0.2126, 0.7152, 0.0722));\n}\n\nfn st_saturation(c : vec3f, amount : f32) -> vec3f {\n return mix(vec3f(st_luma(c)), c, amount);\n}\n\nfn st_tonemap(c : vec3f) -> vec3f {\n let x = max(c, vec3f(0.0));\n return (x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14);\n}\n\n// --- shared post chain -----------------------------------------------------\n// Every shader ends here, so vibrancy / grain / vignette behave identically\n// across the whole catalogue.\nfn st_post(color : vec3f, uv : vec2f) -> vec4f {\n var c = st_saturation(max(color, vec3f(0.0)), params.vibrancy);\n\n if (params.grain > 0.0) {\n let seed = uv * params.resolution + vec2f(params.time * 61.7, params.time * 37.3);\n let n = st_hash21(floor(seed));\n c = c + (n - 0.5) * params.grain;\n }\n\n if (params.vignette > 0.0) {\n let d = length(uv - vec2f(0.5)) * 1.41421356;\n c = c * (1.0 - params.vignette * smoothstep(0.22, 1.0, d));\n }\n\n return vec4f(clamp(c, vec3f(0.0), vec3f(1.0)), 1.0);\n}\n\n// ---------------------------------------------------------------------------\n// liquid-metal\n// ---------------------------------------------------------------------------\n\n// Liquid metal — a warped height field lit like polished chrome.\n// The look lives in the banded environment reflection, not in the noise: keep\n// the field low-frequency, and let the bands do the work.\n\nfn height(p : vec2f, t : f32) -> f32 {\n var q = p;\n q = q + vec2f(st_fbm2(q * 0.9 + vec2f(t * 0.18, 0.0), 3),\n st_fbm2(q * 0.9 + vec2f(0.0, t * 0.15), 3)) * params.warp * 1.6;\n return st_fbm2(q * 1.1 + vec2f(0.0, t * 0.08), 3);\n}\n\n@fragment fn fs_main(@location(0) uv : vec2f) -> @location(0) vec4f {\n let p = st_centered(uv) * params.scale * 2.2;\n let t = st_time();\n\n // Fixed sampling offset in field units: a resolution-derived epsilon makes\n // the surface look completely different in a card and in a hero.\n let eps = 0.012;\n let h = height(p, t);\n let hx = height(p + vec2f(eps, 0.0), t);\n let hy = height(p + vec2f(0.0, eps), t);\n\n let slope = params.a * 0.55;\n let n = normalize(vec3f((h - hx) / eps * slope, (h - hy) / eps * slope, 1.0));\n\n let lightDir = normalize(vec3f(cos(t * 0.35) * 0.7, sin(t * 0.35) * 0.7, 0.9));\n let viewDir = vec3f(0.0, 0.0, 1.0);\n let halfway = normalize(lightDir + viewDir);\n\n let diffuse = clamp(dot(n, lightDir), 0.0, 1.0);\n let spec = pow(clamp(dot(n, halfway), 0.0, 1.0), 16.0 + params.b * 180.0);\n let fresnel = pow(1.0 - clamp(dot(n, viewDir), 0.0, 1.0), 4.0);\n\n // Wide, smooth reflection bands. Reflecting the view around the normal and\n // reading a ramp off it is what separates chrome from rough plastic.\n let reflected = reflect(-viewDir, n);\n let band = reflected.y * 1.6 + reflected.x * 0.5 + t * 0.05;\n let env = st_palette(clamp(band * 0.5 + 0.5, 0.0, 1.0));\n\n // A second, tighter band set gives the hard highlight streaks of polish.\n let streak = pow(abs(sin(band * PI * 1.5)), 8.0 + params.b * 40.0);\n\n var col = env * (0.35 + 0.75 * diffuse) * params.c;\n col = col + st_palette(1.0) * streak * 0.6 * params.c;\n col = col + vec3f(1.0) * spec * params.intensity * 1.4;\n col = col + st_palette(0.9) * fresnel * params.d;\n\n return st_post(st_tonemap(col), uv);\n}\n",
|
|
14
|
+
"mesh-gradient": "// shadertown shared prelude — prepended to every shader at build time.\n// One uniform block is the whole ABI: every shader reads the same struct,\n// so the runtime can swap shaders without changing a binding.\n\nstruct Params {\n resolution : vec2f,\n pointer : vec2f,\n time : f32,\n speed : f32,\n scale : f32,\n intensity : f32,\n vibrancy : f32,\n grain : f32,\n vignette : f32,\n warp : f32,\n a : f32,\n b : f32,\n c : f32,\n d : f32,\n colorA : vec4f,\n colorB : vec4f,\n colorC : vec4f,\n}\n\n@group(0) @binding(0) var<uniform> params : Params;\n\nconst PI : f32 = 3.141592653589793;\nconst TAU : f32 = 6.283185307179586;\n\nfn st_time() -> f32 {\n return params.time * params.speed;\n}\n\n// Centred, aspect-corrected coordinates. y stays in [-0.5, 0.5].\nfn st_centered(uv : vec2f) -> vec2f {\n let aspect = params.resolution.x / max(params.resolution.y, 1.0);\n return vec2f((uv.x - 0.5) * aspect, uv.y - 0.5);\n}\n\nfn st_aspect() -> f32 {\n return params.resolution.x / max(params.resolution.y, 1.0);\n}\n\nfn st_rot(angle : f32) -> mat2x2f {\n let s = sin(angle);\n let c = cos(angle);\n return mat2x2f(c, -s, s, c);\n}\n\n// --- hashing ---------------------------------------------------------------\n\nfn st_hash11(x : f32) -> f32 {\n var p = fract(x * 0.1031);\n p = p * (p + 33.33);\n return fract(p * (p + p));\n}\n\nfn st_hash21(p : vec2f) -> f32 {\n var p3 = fract(vec3f(p.xyx) * 0.1031);\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn st_hash22(p : vec2f) -> vec2f {\n var p3 = fract(vec3f(p.xyx) * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\nfn st_hash33(p : vec3f) -> vec3f {\n var p3 = fract(p * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.xxy + p3.yxx) * p3.zyx);\n}\n\n// --- noise -----------------------------------------------------------------\n\nfn st_noise2(p : vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = st_hash21(i);\n let b = st_hash21(i + vec2f(1.0, 0.0));\n let c = st_hash21(i + vec2f(0.0, 1.0));\n let d = st_hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfn st_noise3(p : vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let n000 = st_hash33(i + vec3f(0.0, 0.0, 0.0)).x;\n let n100 = st_hash33(i + vec3f(1.0, 0.0, 0.0)).x;\n let n010 = st_hash33(i + vec3f(0.0, 1.0, 0.0)).x;\n let n110 = st_hash33(i + vec3f(1.0, 1.0, 0.0)).x;\n let n001 = st_hash33(i + vec3f(0.0, 0.0, 1.0)).x;\n let n101 = st_hash33(i + vec3f(1.0, 0.0, 1.0)).x;\n let n011 = st_hash33(i + vec3f(0.0, 1.0, 1.0)).x;\n let n111 = st_hash33(i + vec3f(1.0, 1.0, 1.0)).x;\n let x00 = mix(n000, n100, u.x);\n let x10 = mix(n010, n110, u.x);\n let x01 = mix(n001, n101, u.x);\n let x11 = mix(n011, n111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\nfn st_fbm2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.5);\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise2(shifted);\n shifted = rotation * shifted * 2.02;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\nfn st_fbm3(p : vec3f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise3(shifted);\n shifted = shifted * 2.03;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// Ridged noise — sharp crests instead of soft hills.\nfn st_ridge2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.7);\n for (var i = 0; i < octaves; i = i + 1) {\n let n = 1.0 - abs(st_noise2(shifted) * 2.0 - 1.0);\n value = value + amplitude * n * n;\n shifted = rotation * shifted * 2.11;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// --- colour ----------------------------------------------------------------\n\n// Three-stop ramp built from the user's colours. t is clamped to [0, 1].\nfn st_palette(t : f32) -> vec3f {\n let x = clamp(t, 0.0, 1.0);\n let low = mix(params.colorA.rgb, params.colorB.rgb, smoothstep(0.0, 0.5, x));\n return mix(low, params.colorC.rgb, smoothstep(0.5, 1.0, x));\n}\n\n// Same ramp, but wrapping — good for cycling and polar shaders.\nfn st_paletteCyclic(t : f32) -> vec3f {\n let x = fract(t);\n let tri = 1.0 - abs(x * 2.0 - 1.0);\n return st_palette(tri);\n}\n\nfn st_luma(c : vec3f) -> f32 {\n return dot(c, vec3f(0.2126, 0.7152, 0.0722));\n}\n\nfn st_saturation(c : vec3f, amount : f32) -> vec3f {\n return mix(vec3f(st_luma(c)), c, amount);\n}\n\nfn st_tonemap(c : vec3f) -> vec3f {\n let x = max(c, vec3f(0.0));\n return (x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14);\n}\n\n// --- shared post chain -----------------------------------------------------\n// Every shader ends here, so vibrancy / grain / vignette behave identically\n// across the whole catalogue.\nfn st_post(color : vec3f, uv : vec2f) -> vec4f {\n var c = st_saturation(max(color, vec3f(0.0)), params.vibrancy);\n\n if (params.grain > 0.0) {\n let seed = uv * params.resolution + vec2f(params.time * 61.7, params.time * 37.3);\n let n = st_hash21(floor(seed));\n c = c + (n - 0.5) * params.grain;\n }\n\n if (params.vignette > 0.0) {\n let d = length(uv - vec2f(0.5)) * 1.41421356;\n c = c * (1.0 - params.vignette * smoothstep(0.22, 1.0, d));\n }\n\n return vec4f(clamp(c, vec3f(0.0), vec3f(1.0)), 1.0);\n}\n\n// ---------------------------------------------------------------------------\n// mesh-gradient\n// ---------------------------------------------------------------------------\n\n// Mesh gradient — soft colour centres orbiting behind frosted glass.\n\n// A gaussian well, raised to a power so nearby colours dominate rather than\n// averaging. Without the exponent every centre contributes everywhere and the\n// middle of the frame turns grey.\nfn blob(p : vec2f, centre : vec2f, radius : f32) -> f32 {\n let d = length(p - centre);\n let g = exp(-d * d / max(radius * radius, 0.0001));\n return g * g * g;\n}\n\n@fragment fn fs_main(@location(0) uv : vec2f) -> @location(0) vec4f {\n var p = st_centered(uv) * params.scale;\n let t = st_time();\n\n // Gentle domain warp keeps the blend from looking like plain radial gradients.\n let warpNoise = vec2f(\n st_fbm2(p * 1.6 + vec2f(0.0, t * 0.12), 3),\n st_fbm2(p * 1.6 + vec2f(5.2, t * 0.12 + 1.3), 3)\n );\n p = p + (warpNoise - 0.5) * params.warp;\n\n let r = params.a;\n let spread = params.b;\n\n let c0 = vec2f(cos(t * 0.31) * spread, sin(t * 0.24) * spread * 0.6);\n let c1 = vec2f(cos(t * 0.19 + 2.1) * spread * 0.9, sin(t * 0.37 + 1.1) * spread * 0.7);\n let c2 = vec2f(cos(t * 0.27 + 4.3) * spread * 0.8, sin(t * 0.21 + 3.7) * spread * 0.75);\n let c3 = vec2f(cos(t * 0.15 + 5.6) * spread, sin(t * 0.33 + 0.4) * spread * 0.5);\n\n // Sharpening the weights before normalising is what keeps the colours\n // distinct. Plain gaussian weights average towards grey in the middle.\n let sharpen = 2.6;\n let w0 = pow(blob(p, c0, r), sharpen);\n let w1 = pow(blob(p, c1, r * 0.9), sharpen);\n let w2 = pow(blob(p, c2, r * 1.15), sharpen);\n let w3 = pow(blob(p, c3, r * 0.8), sharpen);\n let total = w0 + w1 + w2 + w3 + 1e-6;\n\n let base = mix(params.colorA.rgb, params.colorB.rgb, 0.5) * 0.25;\n var col = (params.colorA.rgb * w0\n + params.colorB.rgb * w1\n + params.colorC.rgb * w2\n + mix(params.colorA.rgb, params.colorC.rgb, 0.5) * w3) / total;\n\n // Coverage decides how much of the dark backdrop shows through.\n let coverage = clamp(pow(total, 1.0 / sharpen) * params.c, 0.0, 1.0);\n col = mix(base, col, coverage) * params.intensity;\n\n return st_post(col, uv);\n}\n",
|
|
15
|
+
"neon-grid": "// shadertown shared prelude — prepended to every shader at build time.\n// One uniform block is the whole ABI: every shader reads the same struct,\n// so the runtime can swap shaders without changing a binding.\n\nstruct Params {\n resolution : vec2f,\n pointer : vec2f,\n time : f32,\n speed : f32,\n scale : f32,\n intensity : f32,\n vibrancy : f32,\n grain : f32,\n vignette : f32,\n warp : f32,\n a : f32,\n b : f32,\n c : f32,\n d : f32,\n colorA : vec4f,\n colorB : vec4f,\n colorC : vec4f,\n}\n\n@group(0) @binding(0) var<uniform> params : Params;\n\nconst PI : f32 = 3.141592653589793;\nconst TAU : f32 = 6.283185307179586;\n\nfn st_time() -> f32 {\n return params.time * params.speed;\n}\n\n// Centred, aspect-corrected coordinates. y stays in [-0.5, 0.5].\nfn st_centered(uv : vec2f) -> vec2f {\n let aspect = params.resolution.x / max(params.resolution.y, 1.0);\n return vec2f((uv.x - 0.5) * aspect, uv.y - 0.5);\n}\n\nfn st_aspect() -> f32 {\n return params.resolution.x / max(params.resolution.y, 1.0);\n}\n\nfn st_rot(angle : f32) -> mat2x2f {\n let s = sin(angle);\n let c = cos(angle);\n return mat2x2f(c, -s, s, c);\n}\n\n// --- hashing ---------------------------------------------------------------\n\nfn st_hash11(x : f32) -> f32 {\n var p = fract(x * 0.1031);\n p = p * (p + 33.33);\n return fract(p * (p + p));\n}\n\nfn st_hash21(p : vec2f) -> f32 {\n var p3 = fract(vec3f(p.xyx) * 0.1031);\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn st_hash22(p : vec2f) -> vec2f {\n var p3 = fract(vec3f(p.xyx) * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\nfn st_hash33(p : vec3f) -> vec3f {\n var p3 = fract(p * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.xxy + p3.yxx) * p3.zyx);\n}\n\n// --- noise -----------------------------------------------------------------\n\nfn st_noise2(p : vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = st_hash21(i);\n let b = st_hash21(i + vec2f(1.0, 0.0));\n let c = st_hash21(i + vec2f(0.0, 1.0));\n let d = st_hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfn st_noise3(p : vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let n000 = st_hash33(i + vec3f(0.0, 0.0, 0.0)).x;\n let n100 = st_hash33(i + vec3f(1.0, 0.0, 0.0)).x;\n let n010 = st_hash33(i + vec3f(0.0, 1.0, 0.0)).x;\n let n110 = st_hash33(i + vec3f(1.0, 1.0, 0.0)).x;\n let n001 = st_hash33(i + vec3f(0.0, 0.0, 1.0)).x;\n let n101 = st_hash33(i + vec3f(1.0, 0.0, 1.0)).x;\n let n011 = st_hash33(i + vec3f(0.0, 1.0, 1.0)).x;\n let n111 = st_hash33(i + vec3f(1.0, 1.0, 1.0)).x;\n let x00 = mix(n000, n100, u.x);\n let x10 = mix(n010, n110, u.x);\n let x01 = mix(n001, n101, u.x);\n let x11 = mix(n011, n111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\nfn st_fbm2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.5);\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise2(shifted);\n shifted = rotation * shifted * 2.02;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\nfn st_fbm3(p : vec3f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise3(shifted);\n shifted = shifted * 2.03;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// Ridged noise — sharp crests instead of soft hills.\nfn st_ridge2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.7);\n for (var i = 0; i < octaves; i = i + 1) {\n let n = 1.0 - abs(st_noise2(shifted) * 2.0 - 1.0);\n value = value + amplitude * n * n;\n shifted = rotation * shifted * 2.11;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// --- colour ----------------------------------------------------------------\n\n// Three-stop ramp built from the user's colours. t is clamped to [0, 1].\nfn st_palette(t : f32) -> vec3f {\n let x = clamp(t, 0.0, 1.0);\n let low = mix(params.colorA.rgb, params.colorB.rgb, smoothstep(0.0, 0.5, x));\n return mix(low, params.colorC.rgb, smoothstep(0.5, 1.0, x));\n}\n\n// Same ramp, but wrapping — good for cycling and polar shaders.\nfn st_paletteCyclic(t : f32) -> vec3f {\n let x = fract(t);\n let tri = 1.0 - abs(x * 2.0 - 1.0);\n return st_palette(tri);\n}\n\nfn st_luma(c : vec3f) -> f32 {\n return dot(c, vec3f(0.2126, 0.7152, 0.0722));\n}\n\nfn st_saturation(c : vec3f, amount : f32) -> vec3f {\n return mix(vec3f(st_luma(c)), c, amount);\n}\n\nfn st_tonemap(c : vec3f) -> vec3f {\n let x = max(c, vec3f(0.0));\n return (x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14);\n}\n\n// --- shared post chain -----------------------------------------------------\n// Every shader ends here, so vibrancy / grain / vignette behave identically\n// across the whole catalogue.\nfn st_post(color : vec3f, uv : vec2f) -> vec4f {\n var c = st_saturation(max(color, vec3f(0.0)), params.vibrancy);\n\n if (params.grain > 0.0) {\n let seed = uv * params.resolution + vec2f(params.time * 61.7, params.time * 37.3);\n let n = st_hash21(floor(seed));\n c = c + (n - 0.5) * params.grain;\n }\n\n if (params.vignette > 0.0) {\n let d = length(uv - vec2f(0.5)) * 1.41421356;\n c = c * (1.0 - params.vignette * smoothstep(0.22, 1.0, d));\n }\n\n return vec4f(clamp(c, vec3f(0.0), vec3f(1.0)), 1.0);\n}\n\n// ---------------------------------------------------------------------------\n// neon-grid\n// ---------------------------------------------------------------------------\n\n// Neon grid — perspective floor, banded sun, horizon haze. Synthwave by the book.\n\nfn gridLines(coord : vec2f, weight : f32) -> f32 {\n let width = max(fwidth(coord), vec2f(0.0001));\n let g = abs(fract(coord - 0.5) - 0.5) / width;\n return 1.0 - smoothstep(0.0, weight, min(g.x, g.y));\n}\n\n@fragment fn fs_main(@location(0) uv : vec2f) -> @location(0) vec4f {\n let t = st_time();\n let p = st_centered(uv);\n let horizon = 0.02;\n\n // Floor is evaluated unconditionally: fwidth needs uniform control flow.\n let depth = 1.0 / max(p.y - horizon, 0.004);\n let floorCoord = vec2f(p.x * depth * 0.35, depth * 0.35 - t * params.b);\n let line = gridLines(floorCoord * params.a, 1.8);\n let below = step(horizon, p.y);\n let fade = smoothstep(0.0, 0.09, p.y - horizon) * smoothstep(1.4, 0.12, depth * 0.05);\n\n // Sky above the horizon.\n let skyT = clamp((horizon - p.y) * 2.4, 0.0, 1.0);\n var col = mix(params.colorA.rgb * 0.32, vec3f(0.015, 0.008, 0.05), skyT);\n\n // Sun: a disc sliced by horizontal bands, resting on the horizon.\n let sunCentre = vec2f(0.0, horizon - 0.17);\n let sunDist = length((p - sunCentre) * vec2f(1.0, 1.25));\n let sunMask = smoothstep(0.20, 0.19, sunDist);\n let bands = step(0.38, fract((p.y - sunCentre.y) * 26.0 + t * 0.4))\n * step(sunCentre.y + 0.015, p.y);\n let sunColor = mix(params.colorC.rgb, params.colorB.rgb,\n clamp((p.y - sunCentre.y + 0.2) * 2.5, 0.0, 1.0));\n col = mix(col, sunColor, sunMask * (1.0 - bands));\n col = col + sunColor * smoothstep(0.55, 0.0, sunDist) * 0.3 * params.d;\n\n // Floor: darken the plate, then lay the neon lines over it.\n col = mix(col, col * 0.3, below);\n col = col + params.colorB.rgb * line * fade * below * params.intensity * 1.8;\n\n // Horizon haze.\n col = col + params.colorB.rgb * exp(-abs(p.y - horizon) * 80.0) * params.c;\n\n return st_post(st_tonemap(col), uv);\n}\n",
|
|
16
|
+
"plasma": "// shadertown shared prelude — prepended to every shader at build time.\n// One uniform block is the whole ABI: every shader reads the same struct,\n// so the runtime can swap shaders without changing a binding.\n\nstruct Params {\n resolution : vec2f,\n pointer : vec2f,\n time : f32,\n speed : f32,\n scale : f32,\n intensity : f32,\n vibrancy : f32,\n grain : f32,\n vignette : f32,\n warp : f32,\n a : f32,\n b : f32,\n c : f32,\n d : f32,\n colorA : vec4f,\n colorB : vec4f,\n colorC : vec4f,\n}\n\n@group(0) @binding(0) var<uniform> params : Params;\n\nconst PI : f32 = 3.141592653589793;\nconst TAU : f32 = 6.283185307179586;\n\nfn st_time() -> f32 {\n return params.time * params.speed;\n}\n\n// Centred, aspect-corrected coordinates. y stays in [-0.5, 0.5].\nfn st_centered(uv : vec2f) -> vec2f {\n let aspect = params.resolution.x / max(params.resolution.y, 1.0);\n return vec2f((uv.x - 0.5) * aspect, uv.y - 0.5);\n}\n\nfn st_aspect() -> f32 {\n return params.resolution.x / max(params.resolution.y, 1.0);\n}\n\nfn st_rot(angle : f32) -> mat2x2f {\n let s = sin(angle);\n let c = cos(angle);\n return mat2x2f(c, -s, s, c);\n}\n\n// --- hashing ---------------------------------------------------------------\n\nfn st_hash11(x : f32) -> f32 {\n var p = fract(x * 0.1031);\n p = p * (p + 33.33);\n return fract(p * (p + p));\n}\n\nfn st_hash21(p : vec2f) -> f32 {\n var p3 = fract(vec3f(p.xyx) * 0.1031);\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn st_hash22(p : vec2f) -> vec2f {\n var p3 = fract(vec3f(p.xyx) * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\nfn st_hash33(p : vec3f) -> vec3f {\n var p3 = fract(p * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.xxy + p3.yxx) * p3.zyx);\n}\n\n// --- noise -----------------------------------------------------------------\n\nfn st_noise2(p : vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = st_hash21(i);\n let b = st_hash21(i + vec2f(1.0, 0.0));\n let c = st_hash21(i + vec2f(0.0, 1.0));\n let d = st_hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfn st_noise3(p : vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let n000 = st_hash33(i + vec3f(0.0, 0.0, 0.0)).x;\n let n100 = st_hash33(i + vec3f(1.0, 0.0, 0.0)).x;\n let n010 = st_hash33(i + vec3f(0.0, 1.0, 0.0)).x;\n let n110 = st_hash33(i + vec3f(1.0, 1.0, 0.0)).x;\n let n001 = st_hash33(i + vec3f(0.0, 0.0, 1.0)).x;\n let n101 = st_hash33(i + vec3f(1.0, 0.0, 1.0)).x;\n let n011 = st_hash33(i + vec3f(0.0, 1.0, 1.0)).x;\n let n111 = st_hash33(i + vec3f(1.0, 1.0, 1.0)).x;\n let x00 = mix(n000, n100, u.x);\n let x10 = mix(n010, n110, u.x);\n let x01 = mix(n001, n101, u.x);\n let x11 = mix(n011, n111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\nfn st_fbm2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.5);\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise2(shifted);\n shifted = rotation * shifted * 2.02;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\nfn st_fbm3(p : vec3f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise3(shifted);\n shifted = shifted * 2.03;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// Ridged noise — sharp crests instead of soft hills.\nfn st_ridge2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.7);\n for (var i = 0; i < octaves; i = i + 1) {\n let n = 1.0 - abs(st_noise2(shifted) * 2.0 - 1.0);\n value = value + amplitude * n * n;\n shifted = rotation * shifted * 2.11;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// --- colour ----------------------------------------------------------------\n\n// Three-stop ramp built from the user's colours. t is clamped to [0, 1].\nfn st_palette(t : f32) -> vec3f {\n let x = clamp(t, 0.0, 1.0);\n let low = mix(params.colorA.rgb, params.colorB.rgb, smoothstep(0.0, 0.5, x));\n return mix(low, params.colorC.rgb, smoothstep(0.5, 1.0, x));\n}\n\n// Same ramp, but wrapping — good for cycling and polar shaders.\nfn st_paletteCyclic(t : f32) -> vec3f {\n let x = fract(t);\n let tri = 1.0 - abs(x * 2.0 - 1.0);\n return st_palette(tri);\n}\n\nfn st_luma(c : vec3f) -> f32 {\n return dot(c, vec3f(0.2126, 0.7152, 0.0722));\n}\n\nfn st_saturation(c : vec3f, amount : f32) -> vec3f {\n return mix(vec3f(st_luma(c)), c, amount);\n}\n\nfn st_tonemap(c : vec3f) -> vec3f {\n let x = max(c, vec3f(0.0));\n return (x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14);\n}\n\n// --- shared post chain -----------------------------------------------------\n// Every shader ends here, so vibrancy / grain / vignette behave identically\n// across the whole catalogue.\nfn st_post(color : vec3f, uv : vec2f) -> vec4f {\n var c = st_saturation(max(color, vec3f(0.0)), params.vibrancy);\n\n if (params.grain > 0.0) {\n let seed = uv * params.resolution + vec2f(params.time * 61.7, params.time * 37.3);\n let n = st_hash21(floor(seed));\n c = c + (n - 0.5) * params.grain;\n }\n\n if (params.vignette > 0.0) {\n let d = length(uv - vec2f(0.5)) * 1.41421356;\n c = c * (1.0 - params.vignette * smoothstep(0.22, 1.0, d));\n }\n\n return vec4f(clamp(c, vec3f(0.0), vec3f(1.0)), 1.0);\n}\n\n// ---------------------------------------------------------------------------\n// plasma\n// ---------------------------------------------------------------------------\n\n// Plasma — stacked sine fields, the oldest trick in the demoscene.\n\n@fragment fn fs_main(@location(0) uv : vec2f) -> @location(0) vec4f {\n let p = st_centered(uv) * params.scale * 6.0;\n let t = st_time();\n\n var v = 0.0;\n v = v + sin(p.x * 1.1 + t);\n v = v + sin(p.y * 1.3 + t * 1.17);\n v = v + sin((p.x + p.y) * 0.8 + t * 0.83);\n v = v + sin(length(p * params.a) * 1.4 - t * 1.6);\n\n // Rotating secondary field adds the interference bloom.\n let q = st_rot(t * 0.15 * params.b) * p;\n v = v + sin(q.x * 0.9 - q.y * 1.4 + t * 0.6) * params.c;\n\n let band = v * 0.125 + 0.5 + t * 0.05;\n var col = st_paletteCyclic(band * params.d);\n\n // Contour lines pick out the field's iso-levels.\n let contour = abs(fract(v * 0.9) - 0.5) * 2.0;\n col = col * (1.0 - smoothstep(0.85, 1.0, contour) * 0.55);\n\n return st_post(col * params.intensity, uv);\n}\n",
|
|
17
|
+
"ripple": "// shadertown shared prelude — prepended to every shader at build time.\n// One uniform block is the whole ABI: every shader reads the same struct,\n// so the runtime can swap shaders without changing a binding.\n\nstruct Params {\n resolution : vec2f,\n pointer : vec2f,\n time : f32,\n speed : f32,\n scale : f32,\n intensity : f32,\n vibrancy : f32,\n grain : f32,\n vignette : f32,\n warp : f32,\n a : f32,\n b : f32,\n c : f32,\n d : f32,\n colorA : vec4f,\n colorB : vec4f,\n colorC : vec4f,\n}\n\n@group(0) @binding(0) var<uniform> params : Params;\n\nconst PI : f32 = 3.141592653589793;\nconst TAU : f32 = 6.283185307179586;\n\nfn st_time() -> f32 {\n return params.time * params.speed;\n}\n\n// Centred, aspect-corrected coordinates. y stays in [-0.5, 0.5].\nfn st_centered(uv : vec2f) -> vec2f {\n let aspect = params.resolution.x / max(params.resolution.y, 1.0);\n return vec2f((uv.x - 0.5) * aspect, uv.y - 0.5);\n}\n\nfn st_aspect() -> f32 {\n return params.resolution.x / max(params.resolution.y, 1.0);\n}\n\nfn st_rot(angle : f32) -> mat2x2f {\n let s = sin(angle);\n let c = cos(angle);\n return mat2x2f(c, -s, s, c);\n}\n\n// --- hashing ---------------------------------------------------------------\n\nfn st_hash11(x : f32) -> f32 {\n var p = fract(x * 0.1031);\n p = p * (p + 33.33);\n return fract(p * (p + p));\n}\n\nfn st_hash21(p : vec2f) -> f32 {\n var p3 = fract(vec3f(p.xyx) * 0.1031);\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn st_hash22(p : vec2f) -> vec2f {\n var p3 = fract(vec3f(p.xyx) * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\nfn st_hash33(p : vec3f) -> vec3f {\n var p3 = fract(p * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.xxy + p3.yxx) * p3.zyx);\n}\n\n// --- noise -----------------------------------------------------------------\n\nfn st_noise2(p : vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = st_hash21(i);\n let b = st_hash21(i + vec2f(1.0, 0.0));\n let c = st_hash21(i + vec2f(0.0, 1.0));\n let d = st_hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfn st_noise3(p : vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let n000 = st_hash33(i + vec3f(0.0, 0.0, 0.0)).x;\n let n100 = st_hash33(i + vec3f(1.0, 0.0, 0.0)).x;\n let n010 = st_hash33(i + vec3f(0.0, 1.0, 0.0)).x;\n let n110 = st_hash33(i + vec3f(1.0, 1.0, 0.0)).x;\n let n001 = st_hash33(i + vec3f(0.0, 0.0, 1.0)).x;\n let n101 = st_hash33(i + vec3f(1.0, 0.0, 1.0)).x;\n let n011 = st_hash33(i + vec3f(0.0, 1.0, 1.0)).x;\n let n111 = st_hash33(i + vec3f(1.0, 1.0, 1.0)).x;\n let x00 = mix(n000, n100, u.x);\n let x10 = mix(n010, n110, u.x);\n let x01 = mix(n001, n101, u.x);\n let x11 = mix(n011, n111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\nfn st_fbm2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.5);\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise2(shifted);\n shifted = rotation * shifted * 2.02;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\nfn st_fbm3(p : vec3f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise3(shifted);\n shifted = shifted * 2.03;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// Ridged noise — sharp crests instead of soft hills.\nfn st_ridge2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.7);\n for (var i = 0; i < octaves; i = i + 1) {\n let n = 1.0 - abs(st_noise2(shifted) * 2.0 - 1.0);\n value = value + amplitude * n * n;\n shifted = rotation * shifted * 2.11;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// --- colour ----------------------------------------------------------------\n\n// Three-stop ramp built from the user's colours. t is clamped to [0, 1].\nfn st_palette(t : f32) -> vec3f {\n let x = clamp(t, 0.0, 1.0);\n let low = mix(params.colorA.rgb, params.colorB.rgb, smoothstep(0.0, 0.5, x));\n return mix(low, params.colorC.rgb, smoothstep(0.5, 1.0, x));\n}\n\n// Same ramp, but wrapping — good for cycling and polar shaders.\nfn st_paletteCyclic(t : f32) -> vec3f {\n let x = fract(t);\n let tri = 1.0 - abs(x * 2.0 - 1.0);\n return st_palette(tri);\n}\n\nfn st_luma(c : vec3f) -> f32 {\n return dot(c, vec3f(0.2126, 0.7152, 0.0722));\n}\n\nfn st_saturation(c : vec3f, amount : f32) -> vec3f {\n return mix(vec3f(st_luma(c)), c, amount);\n}\n\nfn st_tonemap(c : vec3f) -> vec3f {\n let x = max(c, vec3f(0.0));\n return (x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14);\n}\n\n// --- shared post chain -----------------------------------------------------\n// Every shader ends here, so vibrancy / grain / vignette behave identically\n// across the whole catalogue.\nfn st_post(color : vec3f, uv : vec2f) -> vec4f {\n var c = st_saturation(max(color, vec3f(0.0)), params.vibrancy);\n\n if (params.grain > 0.0) {\n let seed = uv * params.resolution + vec2f(params.time * 61.7, params.time * 37.3);\n let n = st_hash21(floor(seed));\n c = c + (n - 0.5) * params.grain;\n }\n\n if (params.vignette > 0.0) {\n let d = length(uv - vec2f(0.5)) * 1.41421356;\n c = c * (1.0 - params.vignette * smoothstep(0.22, 1.0, d));\n }\n\n return vec4f(clamp(c, vec3f(0.0), vec3f(1.0)), 1.0);\n}\n\n// ---------------------------------------------------------------------------\n// ripple\n// ---------------------------------------------------------------------------\n\n// Ripple — interference between travelling circular waves.\n\n@fragment fn fs_main(@location(0) uv : vec2f) -> @location(0) vec4f {\n let p = st_centered(uv) * params.scale;\n let t = st_time();\n\n let sourceCount = 5;\n var wave = 0.0;\n var gradient = vec2f(0.0);\n\n for (var i = 0; i < sourceCount; i = i + 1) {\n let fi = f32(i);\n let phase = fi * 2.399963;\n let orbit = 0.28 * params.b;\n let src = vec2f(cos(t * 0.23 + phase) * orbit, sin(t * 0.31 + phase * 1.7) * orbit);\n let delta = p - src;\n let d = length(delta) + 0.0001;\n let k = 40.0 * params.a;\n let decay = 1.0 / (1.0 + d * 6.0);\n let s = sin(d * k - t * 4.0 + phase) * decay;\n wave = wave + s;\n gradient = gradient + (delta / d) * cos(d * k - t * 4.0 + phase) * decay * k;\n }\n\n wave = wave / f32(sourceCount);\n gradient = gradient / f32(sourceCount);\n\n // Treat the wave slope as a refracting surface and read the palette through it.\n let refracted = clamp(0.5 + wave * 1.4 + gradient.x * 0.02 * params.c, 0.0, 1.0);\n var col = st_palette(refracted);\n\n // Caustic highlights where the wavefronts focus.\n let caustic = pow(clamp(1.0 - abs(wave) * 3.2, 0.0, 1.0), 6.0);\n col = col + vec3f(1.0) * caustic * params.d;\n\n return st_post(col * params.intensity, uv);\n}\n",
|
|
18
|
+
"silk": "// shadertown shared prelude — prepended to every shader at build time.\n// One uniform block is the whole ABI: every shader reads the same struct,\n// so the runtime can swap shaders without changing a binding.\n\nstruct Params {\n resolution : vec2f,\n pointer : vec2f,\n time : f32,\n speed : f32,\n scale : f32,\n intensity : f32,\n vibrancy : f32,\n grain : f32,\n vignette : f32,\n warp : f32,\n a : f32,\n b : f32,\n c : f32,\n d : f32,\n colorA : vec4f,\n colorB : vec4f,\n colorC : vec4f,\n}\n\n@group(0) @binding(0) var<uniform> params : Params;\n\nconst PI : f32 = 3.141592653589793;\nconst TAU : f32 = 6.283185307179586;\n\nfn st_time() -> f32 {\n return params.time * params.speed;\n}\n\n// Centred, aspect-corrected coordinates. y stays in [-0.5, 0.5].\nfn st_centered(uv : vec2f) -> vec2f {\n let aspect = params.resolution.x / max(params.resolution.y, 1.0);\n return vec2f((uv.x - 0.5) * aspect, uv.y - 0.5);\n}\n\nfn st_aspect() -> f32 {\n return params.resolution.x / max(params.resolution.y, 1.0);\n}\n\nfn st_rot(angle : f32) -> mat2x2f {\n let s = sin(angle);\n let c = cos(angle);\n return mat2x2f(c, -s, s, c);\n}\n\n// --- hashing ---------------------------------------------------------------\n\nfn st_hash11(x : f32) -> f32 {\n var p = fract(x * 0.1031);\n p = p * (p + 33.33);\n return fract(p * (p + p));\n}\n\nfn st_hash21(p : vec2f) -> f32 {\n var p3 = fract(vec3f(p.xyx) * 0.1031);\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn st_hash22(p : vec2f) -> vec2f {\n var p3 = fract(vec3f(p.xyx) * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\nfn st_hash33(p : vec3f) -> vec3f {\n var p3 = fract(p * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.xxy + p3.yxx) * p3.zyx);\n}\n\n// --- noise -----------------------------------------------------------------\n\nfn st_noise2(p : vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = st_hash21(i);\n let b = st_hash21(i + vec2f(1.0, 0.0));\n let c = st_hash21(i + vec2f(0.0, 1.0));\n let d = st_hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfn st_noise3(p : vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let n000 = st_hash33(i + vec3f(0.0, 0.0, 0.0)).x;\n let n100 = st_hash33(i + vec3f(1.0, 0.0, 0.0)).x;\n let n010 = st_hash33(i + vec3f(0.0, 1.0, 0.0)).x;\n let n110 = st_hash33(i + vec3f(1.0, 1.0, 0.0)).x;\n let n001 = st_hash33(i + vec3f(0.0, 0.0, 1.0)).x;\n let n101 = st_hash33(i + vec3f(1.0, 0.0, 1.0)).x;\n let n011 = st_hash33(i + vec3f(0.0, 1.0, 1.0)).x;\n let n111 = st_hash33(i + vec3f(1.0, 1.0, 1.0)).x;\n let x00 = mix(n000, n100, u.x);\n let x10 = mix(n010, n110, u.x);\n let x01 = mix(n001, n101, u.x);\n let x11 = mix(n011, n111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\nfn st_fbm2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.5);\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise2(shifted);\n shifted = rotation * shifted * 2.02;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\nfn st_fbm3(p : vec3f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise3(shifted);\n shifted = shifted * 2.03;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// Ridged noise — sharp crests instead of soft hills.\nfn st_ridge2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.7);\n for (var i = 0; i < octaves; i = i + 1) {\n let n = 1.0 - abs(st_noise2(shifted) * 2.0 - 1.0);\n value = value + amplitude * n * n;\n shifted = rotation * shifted * 2.11;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// --- colour ----------------------------------------------------------------\n\n// Three-stop ramp built from the user's colours. t is clamped to [0, 1].\nfn st_palette(t : f32) -> vec3f {\n let x = clamp(t, 0.0, 1.0);\n let low = mix(params.colorA.rgb, params.colorB.rgb, smoothstep(0.0, 0.5, x));\n return mix(low, params.colorC.rgb, smoothstep(0.5, 1.0, x));\n}\n\n// Same ramp, but wrapping — good for cycling and polar shaders.\nfn st_paletteCyclic(t : f32) -> vec3f {\n let x = fract(t);\n let tri = 1.0 - abs(x * 2.0 - 1.0);\n return st_palette(tri);\n}\n\nfn st_luma(c : vec3f) -> f32 {\n return dot(c, vec3f(0.2126, 0.7152, 0.0722));\n}\n\nfn st_saturation(c : vec3f, amount : f32) -> vec3f {\n return mix(vec3f(st_luma(c)), c, amount);\n}\n\nfn st_tonemap(c : vec3f) -> vec3f {\n let x = max(c, vec3f(0.0));\n return (x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14);\n}\n\n// --- shared post chain -----------------------------------------------------\n// Every shader ends here, so vibrancy / grain / vignette behave identically\n// across the whole catalogue.\nfn st_post(color : vec3f, uv : vec2f) -> vec4f {\n var c = st_saturation(max(color, vec3f(0.0)), params.vibrancy);\n\n if (params.grain > 0.0) {\n let seed = uv * params.resolution + vec2f(params.time * 61.7, params.time * 37.3);\n let n = st_hash21(floor(seed));\n c = c + (n - 0.5) * params.grain;\n }\n\n if (params.vignette > 0.0) {\n let d = length(uv - vec2f(0.5)) * 1.41421356;\n c = c * (1.0 - params.vignette * smoothstep(0.22, 1.0, d));\n }\n\n return vec4f(clamp(c, vec3f(0.0), vec3f(1.0)), 1.0);\n}\n\n// ---------------------------------------------------------------------------\n// silk\n// ---------------------------------------------------------------------------\n\n// Silk — anisotropic sheen across warped fibre lines. Soft, cloth-like.\n\n@fragment fn fs_main(@location(0) uv : vec2f) -> @location(0) vec4f {\n let t = st_time();\n var p = st_centered(uv) * params.scale;\n\n // Two-stage domain warp gives the fabric its folds.\n let w1 = vec2f(st_fbm2(p * 1.1 + vec2f(0.0, t * 0.08), 4),\n st_fbm2(p * 1.1 + vec2f(3.7, t * 0.06), 4));\n p = p + (w1 - 0.5) * params.warp * 2.0;\n let w2 = st_fbm2(p * 2.6 + vec2f(t * 0.05, 0.0), 5);\n\n // Fibre direction: dense parallel lines bent by the warp.\n let fibres = sin((p.x * 18.0 + p.y * 6.0) * params.a + w2 * 12.0 * params.b);\n let sheen = pow(clamp(fibres * 0.5 + 0.5, 0.0, 1.0), 3.0);\n\n // The base tone comes from the fold height, the highlight from the fibres.\n var col = st_palette(clamp(w2 * 1.4 - 0.1, 0.0, 1.0));\n col = col + st_palette(1.0) * sheen * params.c;\n\n // Slight darkening in the creases sells the depth.\n let crease = smoothstep(0.55, 0.15, w2);\n col = col * (1.0 - crease * params.d * 0.7);\n\n return st_post(col * params.intensity, uv);\n}\n",
|
|
19
|
+
"starfield": "// shadertown shared prelude — prepended to every shader at build time.\n// One uniform block is the whole ABI: every shader reads the same struct,\n// so the runtime can swap shaders without changing a binding.\n\nstruct Params {\n resolution : vec2f,\n pointer : vec2f,\n time : f32,\n speed : f32,\n scale : f32,\n intensity : f32,\n vibrancy : f32,\n grain : f32,\n vignette : f32,\n warp : f32,\n a : f32,\n b : f32,\n c : f32,\n d : f32,\n colorA : vec4f,\n colorB : vec4f,\n colorC : vec4f,\n}\n\n@group(0) @binding(0) var<uniform> params : Params;\n\nconst PI : f32 = 3.141592653589793;\nconst TAU : f32 = 6.283185307179586;\n\nfn st_time() -> f32 {\n return params.time * params.speed;\n}\n\n// Centred, aspect-corrected coordinates. y stays in [-0.5, 0.5].\nfn st_centered(uv : vec2f) -> vec2f {\n let aspect = params.resolution.x / max(params.resolution.y, 1.0);\n return vec2f((uv.x - 0.5) * aspect, uv.y - 0.5);\n}\n\nfn st_aspect() -> f32 {\n return params.resolution.x / max(params.resolution.y, 1.0);\n}\n\nfn st_rot(angle : f32) -> mat2x2f {\n let s = sin(angle);\n let c = cos(angle);\n return mat2x2f(c, -s, s, c);\n}\n\n// --- hashing ---------------------------------------------------------------\n\nfn st_hash11(x : f32) -> f32 {\n var p = fract(x * 0.1031);\n p = p * (p + 33.33);\n return fract(p * (p + p));\n}\n\nfn st_hash21(p : vec2f) -> f32 {\n var p3 = fract(vec3f(p.xyx) * 0.1031);\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn st_hash22(p : vec2f) -> vec2f {\n var p3 = fract(vec3f(p.xyx) * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\nfn st_hash33(p : vec3f) -> vec3f {\n var p3 = fract(p * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.xxy + p3.yxx) * p3.zyx);\n}\n\n// --- noise -----------------------------------------------------------------\n\nfn st_noise2(p : vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = st_hash21(i);\n let b = st_hash21(i + vec2f(1.0, 0.0));\n let c = st_hash21(i + vec2f(0.0, 1.0));\n let d = st_hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfn st_noise3(p : vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let n000 = st_hash33(i + vec3f(0.0, 0.0, 0.0)).x;\n let n100 = st_hash33(i + vec3f(1.0, 0.0, 0.0)).x;\n let n010 = st_hash33(i + vec3f(0.0, 1.0, 0.0)).x;\n let n110 = st_hash33(i + vec3f(1.0, 1.0, 0.0)).x;\n let n001 = st_hash33(i + vec3f(0.0, 0.0, 1.0)).x;\n let n101 = st_hash33(i + vec3f(1.0, 0.0, 1.0)).x;\n let n011 = st_hash33(i + vec3f(0.0, 1.0, 1.0)).x;\n let n111 = st_hash33(i + vec3f(1.0, 1.0, 1.0)).x;\n let x00 = mix(n000, n100, u.x);\n let x10 = mix(n010, n110, u.x);\n let x01 = mix(n001, n101, u.x);\n let x11 = mix(n011, n111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\nfn st_fbm2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.5);\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise2(shifted);\n shifted = rotation * shifted * 2.02;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\nfn st_fbm3(p : vec3f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise3(shifted);\n shifted = shifted * 2.03;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// Ridged noise — sharp crests instead of soft hills.\nfn st_ridge2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.7);\n for (var i = 0; i < octaves; i = i + 1) {\n let n = 1.0 - abs(st_noise2(shifted) * 2.0 - 1.0);\n value = value + amplitude * n * n;\n shifted = rotation * shifted * 2.11;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// --- colour ----------------------------------------------------------------\n\n// Three-stop ramp built from the user's colours. t is clamped to [0, 1].\nfn st_palette(t : f32) -> vec3f {\n let x = clamp(t, 0.0, 1.0);\n let low = mix(params.colorA.rgb, params.colorB.rgb, smoothstep(0.0, 0.5, x));\n return mix(low, params.colorC.rgb, smoothstep(0.5, 1.0, x));\n}\n\n// Same ramp, but wrapping — good for cycling and polar shaders.\nfn st_paletteCyclic(t : f32) -> vec3f {\n let x = fract(t);\n let tri = 1.0 - abs(x * 2.0 - 1.0);\n return st_palette(tri);\n}\n\nfn st_luma(c : vec3f) -> f32 {\n return dot(c, vec3f(0.2126, 0.7152, 0.0722));\n}\n\nfn st_saturation(c : vec3f, amount : f32) -> vec3f {\n return mix(vec3f(st_luma(c)), c, amount);\n}\n\nfn st_tonemap(c : vec3f) -> vec3f {\n let x = max(c, vec3f(0.0));\n return (x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14);\n}\n\n// --- shared post chain -----------------------------------------------------\n// Every shader ends here, so vibrancy / grain / vignette behave identically\n// across the whole catalogue.\nfn st_post(color : vec3f, uv : vec2f) -> vec4f {\n var c = st_saturation(max(color, vec3f(0.0)), params.vibrancy);\n\n if (params.grain > 0.0) {\n let seed = uv * params.resolution + vec2f(params.time * 61.7, params.time * 37.3);\n let n = st_hash21(floor(seed));\n c = c + (n - 0.5) * params.grain;\n }\n\n if (params.vignette > 0.0) {\n let d = length(uv - vec2f(0.5)) * 1.41421356;\n c = c * (1.0 - params.vignette * smoothstep(0.22, 1.0, d));\n }\n\n return vec4f(clamp(c, vec3f(0.0), vec3f(1.0)), 1.0);\n}\n\n// ---------------------------------------------------------------------------\n// starfield\n// ---------------------------------------------------------------------------\n\n// Starfield — a warp corridor. Layered depth slices flying at the camera.\n\n// Two independent hashes per cell; sharing one seed across layers is what\n// produces the diagonal moiré you get from adding a scalar to both components.\nfn cellRandom(cell : vec2f, layerSeed : f32) -> vec3f {\n let a = st_hash22(cell + vec2f(layerSeed * 37.19, layerSeed * 11.73));\n let b = st_hash21(cell.yx + layerSeed * 91.31);\n return vec3f(a, b);\n}\n\nfn layer(p : vec2f, layerIndex : f32, t : f32) -> vec3f {\n let z = fract(layerIndex * 0.1667 - t * 0.12);\n let scaleByDepth = mix(16.0, 2.0, z);\n let fade = smoothstep(0.0, 0.22, z) * smoothstep(1.0, 0.7, z);\n if (fade <= 0.0) { return vec3f(0.0); }\n\n // Rotating each slice breaks the alignment between neighbouring layers.\n let grid = st_rot(layerIndex * 1.37) * p * scaleByDepth;\n let base = floor(grid);\n let frac = fract(grid) - 0.5;\n\n var acc = vec3f(0.0);\n for (var y = -1; y <= 1; y = y + 1) {\n for (var x = -1; x <= 1; x = x + 1) {\n let neighbour = vec2f(f32(x), f32(y));\n let rnd = cellRandom(base + neighbour, layerIndex);\n if (rnd.z < 0.72) { continue; }\n\n let point = neighbour + rnd.xy - 0.5;\n var offset = frac - point;\n // Motion blur: stretch each star along its radial direction.\n let radial = normalize(p + vec2f(1e-5));\n let along = dot(offset, radial);\n let across = offset - radial * along;\n offset = radial * along / (1.0 + params.b * 8.0 * (1.0 - z)) + across;\n\n // A tight inverse-square core keeps stars as points. Widen the epsilon\n // and six layers of them blur into fog.\n let d = length(offset);\n let brightness = 0.00022 * params.a * (0.35 + rnd.z);\n acc = acc + st_palette(rnd.y) * (brightness / (d * d + 0.000018)) * fade;\n }\n }\n return acc;\n}\n\n@fragment fn fs_main(@location(0) uv : vec2f) -> @location(0) vec4f {\n var p = st_centered(uv) * params.scale;\n let t = st_time();\n p = st_rot(t * 0.05 * params.d) * p;\n\n var col = vec3f(0.004, 0.005, 0.012);\n for (var i = 0; i < 6; i = i + 1) {\n col = col + layer(p, f32(i), t);\n }\n\n // Core glow at the vanishing point.\n let core = params.c * 0.05 / (length(p) + 0.06);\n col = col + st_palette(1.0) * core;\n\n return st_post(st_tonemap(col * params.intensity), uv);\n}\n",
|
|
20
|
+
"truchet": "// shadertown shared prelude — prepended to every shader at build time.\n// One uniform block is the whole ABI: every shader reads the same struct,\n// so the runtime can swap shaders without changing a binding.\n\nstruct Params {\n resolution : vec2f,\n pointer : vec2f,\n time : f32,\n speed : f32,\n scale : f32,\n intensity : f32,\n vibrancy : f32,\n grain : f32,\n vignette : f32,\n warp : f32,\n a : f32,\n b : f32,\n c : f32,\n d : f32,\n colorA : vec4f,\n colorB : vec4f,\n colorC : vec4f,\n}\n\n@group(0) @binding(0) var<uniform> params : Params;\n\nconst PI : f32 = 3.141592653589793;\nconst TAU : f32 = 6.283185307179586;\n\nfn st_time() -> f32 {\n return params.time * params.speed;\n}\n\n// Centred, aspect-corrected coordinates. y stays in [-0.5, 0.5].\nfn st_centered(uv : vec2f) -> vec2f {\n let aspect = params.resolution.x / max(params.resolution.y, 1.0);\n return vec2f((uv.x - 0.5) * aspect, uv.y - 0.5);\n}\n\nfn st_aspect() -> f32 {\n return params.resolution.x / max(params.resolution.y, 1.0);\n}\n\nfn st_rot(angle : f32) -> mat2x2f {\n let s = sin(angle);\n let c = cos(angle);\n return mat2x2f(c, -s, s, c);\n}\n\n// --- hashing ---------------------------------------------------------------\n\nfn st_hash11(x : f32) -> f32 {\n var p = fract(x * 0.1031);\n p = p * (p + 33.33);\n return fract(p * (p + p));\n}\n\nfn st_hash21(p : vec2f) -> f32 {\n var p3 = fract(vec3f(p.xyx) * 0.1031);\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn st_hash22(p : vec2f) -> vec2f {\n var p3 = fract(vec3f(p.xyx) * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\nfn st_hash33(p : vec3f) -> vec3f {\n var p3 = fract(p * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.xxy + p3.yxx) * p3.zyx);\n}\n\n// --- noise -----------------------------------------------------------------\n\nfn st_noise2(p : vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = st_hash21(i);\n let b = st_hash21(i + vec2f(1.0, 0.0));\n let c = st_hash21(i + vec2f(0.0, 1.0));\n let d = st_hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfn st_noise3(p : vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let n000 = st_hash33(i + vec3f(0.0, 0.0, 0.0)).x;\n let n100 = st_hash33(i + vec3f(1.0, 0.0, 0.0)).x;\n let n010 = st_hash33(i + vec3f(0.0, 1.0, 0.0)).x;\n let n110 = st_hash33(i + vec3f(1.0, 1.0, 0.0)).x;\n let n001 = st_hash33(i + vec3f(0.0, 0.0, 1.0)).x;\n let n101 = st_hash33(i + vec3f(1.0, 0.0, 1.0)).x;\n let n011 = st_hash33(i + vec3f(0.0, 1.0, 1.0)).x;\n let n111 = st_hash33(i + vec3f(1.0, 1.0, 1.0)).x;\n let x00 = mix(n000, n100, u.x);\n let x10 = mix(n010, n110, u.x);\n let x01 = mix(n001, n101, u.x);\n let x11 = mix(n011, n111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\nfn st_fbm2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.5);\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise2(shifted);\n shifted = rotation * shifted * 2.02;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\nfn st_fbm3(p : vec3f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise3(shifted);\n shifted = shifted * 2.03;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// Ridged noise — sharp crests instead of soft hills.\nfn st_ridge2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.7);\n for (var i = 0; i < octaves; i = i + 1) {\n let n = 1.0 - abs(st_noise2(shifted) * 2.0 - 1.0);\n value = value + amplitude * n * n;\n shifted = rotation * shifted * 2.11;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// --- colour ----------------------------------------------------------------\n\n// Three-stop ramp built from the user's colours. t is clamped to [0, 1].\nfn st_palette(t : f32) -> vec3f {\n let x = clamp(t, 0.0, 1.0);\n let low = mix(params.colorA.rgb, params.colorB.rgb, smoothstep(0.0, 0.5, x));\n return mix(low, params.colorC.rgb, smoothstep(0.5, 1.0, x));\n}\n\n// Same ramp, but wrapping — good for cycling and polar shaders.\nfn st_paletteCyclic(t : f32) -> vec3f {\n let x = fract(t);\n let tri = 1.0 - abs(x * 2.0 - 1.0);\n return st_palette(tri);\n}\n\nfn st_luma(c : vec3f) -> f32 {\n return dot(c, vec3f(0.2126, 0.7152, 0.0722));\n}\n\nfn st_saturation(c : vec3f, amount : f32) -> vec3f {\n return mix(vec3f(st_luma(c)), c, amount);\n}\n\nfn st_tonemap(c : vec3f) -> vec3f {\n let x = max(c, vec3f(0.0));\n return (x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14);\n}\n\n// --- shared post chain -----------------------------------------------------\n// Every shader ends here, so vibrancy / grain / vignette behave identically\n// across the whole catalogue.\nfn st_post(color : vec3f, uv : vec2f) -> vec4f {\n var c = st_saturation(max(color, vec3f(0.0)), params.vibrancy);\n\n if (params.grain > 0.0) {\n let seed = uv * params.resolution + vec2f(params.time * 61.7, params.time * 37.3);\n let n = st_hash21(floor(seed));\n c = c + (n - 0.5) * params.grain;\n }\n\n if (params.vignette > 0.0) {\n let d = length(uv - vec2f(0.5)) * 1.41421356;\n c = c * (1.0 - params.vignette * smoothstep(0.22, 1.0, d));\n }\n\n return vec4f(clamp(c, vec3f(0.0), vec3f(1.0)), 1.0);\n}\n\n// ---------------------------------------------------------------------------\n// truchet\n// ---------------------------------------------------------------------------\n\n// Truchet — quarter-arc tiles that flip in place. Hard, graphic, no noise wash.\n\nfn arcTile(f : vec2f, flipped : bool, weight : f32) -> f32 {\n var q = f;\n if (flipped) { q.x = 1.0 - q.x; }\n let d1 = abs(length(q) - 0.5);\n let d2 = abs(length(q - vec2f(1.0, 1.0)) - 0.5);\n let d = min(d1, d2);\n return 1.0 - smoothstep(weight, weight + 0.02, d);\n}\n\n@fragment fn fs_main(@location(0) uv : vec2f) -> @location(0) vec4f {\n let t = st_time();\n var p = st_centered(uv) * params.scale * 6.0;\n p = st_rot(t * 0.04 * params.d) * p;\n p = p + vec2f(t * 0.25 * params.c, 0.0);\n\n let base = floor(p);\n let f = fract(p);\n\n // Each tile flips on its own clock.\n let rnd = st_hash21(base);\n let phase = sin(t * 0.9 + rnd * TAU) * 0.5 + 0.5;\n let flipped = (rnd + phase * params.b) > 1.0;\n\n let line = arcTile(f, flipped, params.a * 0.12);\n\n let tint = st_palette(fract(rnd + t * 0.05));\n let bg = params.colorA.rgb * 0.14;\n var col = mix(bg, tint * params.intensity, line);\n\n // A soft bloom off the strokes keeps it from looking flat.\n col = col + tint * line * 0.25;\n\n return st_post(col, uv);\n}\n",
|
|
21
|
+
"tunnel": "// shadertown shared prelude — prepended to every shader at build time.\n// One uniform block is the whole ABI: every shader reads the same struct,\n// so the runtime can swap shaders without changing a binding.\n\nstruct Params {\n resolution : vec2f,\n pointer : vec2f,\n time : f32,\n speed : f32,\n scale : f32,\n intensity : f32,\n vibrancy : f32,\n grain : f32,\n vignette : f32,\n warp : f32,\n a : f32,\n b : f32,\n c : f32,\n d : f32,\n colorA : vec4f,\n colorB : vec4f,\n colorC : vec4f,\n}\n\n@group(0) @binding(0) var<uniform> params : Params;\n\nconst PI : f32 = 3.141592653589793;\nconst TAU : f32 = 6.283185307179586;\n\nfn st_time() -> f32 {\n return params.time * params.speed;\n}\n\n// Centred, aspect-corrected coordinates. y stays in [-0.5, 0.5].\nfn st_centered(uv : vec2f) -> vec2f {\n let aspect = params.resolution.x / max(params.resolution.y, 1.0);\n return vec2f((uv.x - 0.5) * aspect, uv.y - 0.5);\n}\n\nfn st_aspect() -> f32 {\n return params.resolution.x / max(params.resolution.y, 1.0);\n}\n\nfn st_rot(angle : f32) -> mat2x2f {\n let s = sin(angle);\n let c = cos(angle);\n return mat2x2f(c, -s, s, c);\n}\n\n// --- hashing ---------------------------------------------------------------\n\nfn st_hash11(x : f32) -> f32 {\n var p = fract(x * 0.1031);\n p = p * (p + 33.33);\n return fract(p * (p + p));\n}\n\nfn st_hash21(p : vec2f) -> f32 {\n var p3 = fract(vec3f(p.xyx) * 0.1031);\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn st_hash22(p : vec2f) -> vec2f {\n var p3 = fract(vec3f(p.xyx) * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\nfn st_hash33(p : vec3f) -> vec3f {\n var p3 = fract(p * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.xxy + p3.yxx) * p3.zyx);\n}\n\n// --- noise -----------------------------------------------------------------\n\nfn st_noise2(p : vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = st_hash21(i);\n let b = st_hash21(i + vec2f(1.0, 0.0));\n let c = st_hash21(i + vec2f(0.0, 1.0));\n let d = st_hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfn st_noise3(p : vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let n000 = st_hash33(i + vec3f(0.0, 0.0, 0.0)).x;\n let n100 = st_hash33(i + vec3f(1.0, 0.0, 0.0)).x;\n let n010 = st_hash33(i + vec3f(0.0, 1.0, 0.0)).x;\n let n110 = st_hash33(i + vec3f(1.0, 1.0, 0.0)).x;\n let n001 = st_hash33(i + vec3f(0.0, 0.0, 1.0)).x;\n let n101 = st_hash33(i + vec3f(1.0, 0.0, 1.0)).x;\n let n011 = st_hash33(i + vec3f(0.0, 1.0, 1.0)).x;\n let n111 = st_hash33(i + vec3f(1.0, 1.0, 1.0)).x;\n let x00 = mix(n000, n100, u.x);\n let x10 = mix(n010, n110, u.x);\n let x01 = mix(n001, n101, u.x);\n let x11 = mix(n011, n111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\nfn st_fbm2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.5);\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise2(shifted);\n shifted = rotation * shifted * 2.02;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\nfn st_fbm3(p : vec3f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise3(shifted);\n shifted = shifted * 2.03;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// Ridged noise — sharp crests instead of soft hills.\nfn st_ridge2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.7);\n for (var i = 0; i < octaves; i = i + 1) {\n let n = 1.0 - abs(st_noise2(shifted) * 2.0 - 1.0);\n value = value + amplitude * n * n;\n shifted = rotation * shifted * 2.11;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// --- colour ----------------------------------------------------------------\n\n// Three-stop ramp built from the user's colours. t is clamped to [0, 1].\nfn st_palette(t : f32) -> vec3f {\n let x = clamp(t, 0.0, 1.0);\n let low = mix(params.colorA.rgb, params.colorB.rgb, smoothstep(0.0, 0.5, x));\n return mix(low, params.colorC.rgb, smoothstep(0.5, 1.0, x));\n}\n\n// Same ramp, but wrapping — good for cycling and polar shaders.\nfn st_paletteCyclic(t : f32) -> vec3f {\n let x = fract(t);\n let tri = 1.0 - abs(x * 2.0 - 1.0);\n return st_palette(tri);\n}\n\nfn st_luma(c : vec3f) -> f32 {\n return dot(c, vec3f(0.2126, 0.7152, 0.0722));\n}\n\nfn st_saturation(c : vec3f, amount : f32) -> vec3f {\n return mix(vec3f(st_luma(c)), c, amount);\n}\n\nfn st_tonemap(c : vec3f) -> vec3f {\n let x = max(c, vec3f(0.0));\n return (x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14);\n}\n\n// --- shared post chain -----------------------------------------------------\n// Every shader ends here, so vibrancy / grain / vignette behave identically\n// across the whole catalogue.\nfn st_post(color : vec3f, uv : vec2f) -> vec4f {\n var c = st_saturation(max(color, vec3f(0.0)), params.vibrancy);\n\n if (params.grain > 0.0) {\n let seed = uv * params.resolution + vec2f(params.time * 61.7, params.time * 37.3);\n let n = st_hash21(floor(seed));\n c = c + (n - 0.5) * params.grain;\n }\n\n if (params.vignette > 0.0) {\n let d = length(uv - vec2f(0.5)) * 1.41421356;\n c = c * (1.0 - params.vignette * smoothstep(0.22, 1.0, d));\n }\n\n return vec4f(clamp(c, vec3f(0.0), vec3f(1.0)), 1.0);\n}\n\n// ---------------------------------------------------------------------------\n// tunnel\n// ---------------------------------------------------------------------------\n\n// Tunnel — a raymarched corridor with emissive rings and distance fog.\n\n// The corridor centre wanders as you fly down it. Keep the amplitude well\n// under the radius, or the ray leaves the tube before it has travelled.\nfn tunnelPath(z : f32) -> vec2f {\n return vec2f(sin(z * 0.28) * 0.42, cos(z * 0.21) * 0.34);\n}\n\n// Distance from a point to the wall, measured from inside the tube.\nfn sceneSdf(pos : vec3f, radius : f32, ribs : f32) -> f32 {\n let q = pos.xy - tunnelPath(pos.z);\n let r = length(q);\n let theta = atan2(q.y, q.x);\n let corrugation = sin(pos.z * 2.4) * 0.05 * ribs + sin(theta * 6.0 + pos.z * 0.5) * 0.05 * ribs;\n return radius + corrugation - r;\n}\n\n@fragment fn fs_main(@location(0) uv : vec2f) -> @location(0) vec4f {\n let p = st_centered(uv) * 2.0;\n let t = st_time() * 2.0;\n\n let ro = vec3f(tunnelPath(t), t);\n // Aim slightly along the corridor so the walls sweep past rather than\n // sitting still around a fixed vanishing point.\n let ahead = tunnelPath(t + 2.0) - tunnelPath(t);\n let rd = normalize(vec3f(p / max(params.scale, 0.2) + ahead * 0.45, 1.6));\n\n var travelled = 0.05;\n var hit = false;\n for (var i = 0; i < 96; i = i + 1) {\n let d = sceneSdf(ro + rd * travelled, params.a, params.b);\n if (d < 0.004 * travelled) {\n hit = true;\n break;\n }\n travelled = travelled + max(d * 0.7, 0.01);\n if (travelled > 30.0) { break; }\n }\n\n if (!hit) {\n return st_post(vec3f(0.0), uv);\n }\n\n let pos = ro + rd * travelled;\n let q = pos.xy - tunnelPath(pos.z);\n let theta = atan2(q.y, q.x);\n\n // Wall texture in tunnel space: seams around, plates along. Both are thin\n // lines on a dark wall — wide bands read as a kaleidoscope, not a corridor.\n let seams = 1.0 - smoothstep(0.0, 0.16, abs(fract(theta / TAU * 8.0) - 0.5) * 2.0);\n let plates = 1.0 - smoothstep(0.0, 0.2, abs(fract(pos.z * 0.9) - 0.5) * 2.0);\n let grime = st_fbm2(vec2f(theta * 2.4, pos.z * 0.7), 3);\n let surface = clamp(0.12 + grime * 0.35 + max(seams, plates) * 0.55, 0.0, 1.0);\n\n // Cheap normal from the SDF gradient, lit from a lamp at the camera.\n let e = vec2f(0.01, 0.0);\n let n = normalize(vec3f(\n sceneSdf(pos + e.xyy, params.a, params.b) - sceneSdf(pos - e.xyy, params.a, params.b),\n sceneSdf(pos + e.yxy, params.a, params.b) - sceneSdf(pos - e.yxy, params.a, params.b),\n sceneSdf(pos + e.yyx, params.a, params.b) - sceneSdf(pos - e.yyx, params.a, params.b),\n ));\n let lamp = clamp(dot(n, -rd), 0.0, 1.0);\n\n // Falling off with distance as well as with the lamp angle is what makes\n // the near wall read as near.\n let nearness = 1.0 / (1.0 + travelled * 0.35);\n var col = st_palette(clamp(surface, 0.0, 1.0))\n * (0.10 + 1.15 * lamp * nearness) * params.intensity;\n\n // Emissive rings at fixed intervals down the corridor.\n let ring = pow(1.0 - abs(fract(pos.z * 0.5) * 2.0 - 1.0), 20.0);\n col = col + st_palette(1.0) * ring * params.c * 5.0;\n\n // Fog swallows the far end.\n col = col * exp(-travelled * 0.10 * (0.4 + params.d * 2.4));\n\n return st_post(st_tonemap(col), uv);\n}\n",
|
|
22
|
+
"voronoi": "// shadertown shared prelude — prepended to every shader at build time.\n// One uniform block is the whole ABI: every shader reads the same struct,\n// so the runtime can swap shaders without changing a binding.\n\nstruct Params {\n resolution : vec2f,\n pointer : vec2f,\n time : f32,\n speed : f32,\n scale : f32,\n intensity : f32,\n vibrancy : f32,\n grain : f32,\n vignette : f32,\n warp : f32,\n a : f32,\n b : f32,\n c : f32,\n d : f32,\n colorA : vec4f,\n colorB : vec4f,\n colorC : vec4f,\n}\n\n@group(0) @binding(0) var<uniform> params : Params;\n\nconst PI : f32 = 3.141592653589793;\nconst TAU : f32 = 6.283185307179586;\n\nfn st_time() -> f32 {\n return params.time * params.speed;\n}\n\n// Centred, aspect-corrected coordinates. y stays in [-0.5, 0.5].\nfn st_centered(uv : vec2f) -> vec2f {\n let aspect = params.resolution.x / max(params.resolution.y, 1.0);\n return vec2f((uv.x - 0.5) * aspect, uv.y - 0.5);\n}\n\nfn st_aspect() -> f32 {\n return params.resolution.x / max(params.resolution.y, 1.0);\n}\n\nfn st_rot(angle : f32) -> mat2x2f {\n let s = sin(angle);\n let c = cos(angle);\n return mat2x2f(c, -s, s, c);\n}\n\n// --- hashing ---------------------------------------------------------------\n\nfn st_hash11(x : f32) -> f32 {\n var p = fract(x * 0.1031);\n p = p * (p + 33.33);\n return fract(p * (p + p));\n}\n\nfn st_hash21(p : vec2f) -> f32 {\n var p3 = fract(vec3f(p.xyx) * 0.1031);\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\nfn st_hash22(p : vec2f) -> vec2f {\n var p3 = fract(vec3f(p.xyx) * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\nfn st_hash33(p : vec3f) -> vec3f {\n var p3 = fract(p * vec3f(0.1031, 0.1030, 0.0973));\n p3 = p3 + dot(p3, p3.yxz + 33.33);\n return fract((p3.xxy + p3.yxx) * p3.zyx);\n}\n\n// --- noise -----------------------------------------------------------------\n\nfn st_noise2(p : vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = st_hash21(i);\n let b = st_hash21(i + vec2f(1.0, 0.0));\n let c = st_hash21(i + vec2f(0.0, 1.0));\n let d = st_hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfn st_noise3(p : vec3f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let n000 = st_hash33(i + vec3f(0.0, 0.0, 0.0)).x;\n let n100 = st_hash33(i + vec3f(1.0, 0.0, 0.0)).x;\n let n010 = st_hash33(i + vec3f(0.0, 1.0, 0.0)).x;\n let n110 = st_hash33(i + vec3f(1.0, 1.0, 0.0)).x;\n let n001 = st_hash33(i + vec3f(0.0, 0.0, 1.0)).x;\n let n101 = st_hash33(i + vec3f(1.0, 0.0, 1.0)).x;\n let n011 = st_hash33(i + vec3f(0.0, 1.0, 1.0)).x;\n let n111 = st_hash33(i + vec3f(1.0, 1.0, 1.0)).x;\n let x00 = mix(n000, n100, u.x);\n let x10 = mix(n010, n110, u.x);\n let x01 = mix(n001, n101, u.x);\n let x11 = mix(n011, n111, u.x);\n return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);\n}\n\nfn st_fbm2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.5);\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise2(shifted);\n shifted = rotation * shifted * 2.02;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\nfn st_fbm3(p : vec3f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n for (var i = 0; i < octaves; i = i + 1) {\n value = value + amplitude * st_noise3(shifted);\n shifted = shifted * 2.03;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// Ridged noise — sharp crests instead of soft hills.\nfn st_ridge2(p : vec2f, octaves : i32) -> f32 {\n var value = 0.0;\n var amplitude = 0.5;\n var shifted = p;\n let rotation = st_rot(0.7);\n for (var i = 0; i < octaves; i = i + 1) {\n let n = 1.0 - abs(st_noise2(shifted) * 2.0 - 1.0);\n value = value + amplitude * n * n;\n shifted = rotation * shifted * 2.11;\n amplitude = amplitude * 0.5;\n }\n return value;\n}\n\n// --- colour ----------------------------------------------------------------\n\n// Three-stop ramp built from the user's colours. t is clamped to [0, 1].\nfn st_palette(t : f32) -> vec3f {\n let x = clamp(t, 0.0, 1.0);\n let low = mix(params.colorA.rgb, params.colorB.rgb, smoothstep(0.0, 0.5, x));\n return mix(low, params.colorC.rgb, smoothstep(0.5, 1.0, x));\n}\n\n// Same ramp, but wrapping — good for cycling and polar shaders.\nfn st_paletteCyclic(t : f32) -> vec3f {\n let x = fract(t);\n let tri = 1.0 - abs(x * 2.0 - 1.0);\n return st_palette(tri);\n}\n\nfn st_luma(c : vec3f) -> f32 {\n return dot(c, vec3f(0.2126, 0.7152, 0.0722));\n}\n\nfn st_saturation(c : vec3f, amount : f32) -> vec3f {\n return mix(vec3f(st_luma(c)), c, amount);\n}\n\nfn st_tonemap(c : vec3f) -> vec3f {\n let x = max(c, vec3f(0.0));\n return (x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14);\n}\n\n// --- shared post chain -----------------------------------------------------\n// Every shader ends here, so vibrancy / grain / vignette behave identically\n// across the whole catalogue.\nfn st_post(color : vec3f, uv : vec2f) -> vec4f {\n var c = st_saturation(max(color, vec3f(0.0)), params.vibrancy);\n\n if (params.grain > 0.0) {\n let seed = uv * params.resolution + vec2f(params.time * 61.7, params.time * 37.3);\n let n = st_hash21(floor(seed));\n c = c + (n - 0.5) * params.grain;\n }\n\n if (params.vignette > 0.0) {\n let d = length(uv - vec2f(0.5)) * 1.41421356;\n c = c * (1.0 - params.vignette * smoothstep(0.22, 1.0, d));\n }\n\n return vec4f(clamp(c, vec3f(0.0), vec3f(1.0)), 1.0);\n}\n\n// ---------------------------------------------------------------------------\n// voronoi\n// ---------------------------------------------------------------------------\n\n// Voronoi — animated cells with lit edges. Crystalline, hard-edged.\n\nstruct Cell {\n f1 : f32,\n f2 : f32,\n id : vec2f,\n}\n\nfn voronoi(p : vec2f, t : f32, jitter : f32) -> Cell {\n let base = floor(p);\n let frac = fract(p);\n var f1 = 8.0;\n var f2 = 8.0;\n var id = vec2f(0.0);\n\n for (var y = -1; y <= 1; y = y + 1) {\n for (var x = -1; x <= 1; x = x + 1) {\n let neighbour = vec2f(f32(x), f32(y));\n let rnd = st_hash22(base + neighbour);\n let point = neighbour + 0.5 + (sin(t + rnd * TAU) * 0.5) * jitter;\n let d = length(point - frac);\n if (d < f1) {\n f2 = f1;\n f1 = d;\n id = base + neighbour;\n } else if (d < f2) {\n f2 = d;\n }\n }\n }\n return Cell(f1, f2, id);\n}\n\n@fragment fn fs_main(@location(0) uv : vec2f) -> @location(0) vec4f {\n let p = st_centered(uv) * params.scale * 8.0;\n let t = st_time();\n\n let cell = voronoi(p, t, params.a);\n let edge = cell.f2 - cell.f1;\n\n let tone = st_hash21(cell.id);\n var col = st_palette(fract(tone + t * 0.03 * params.d)) * (0.25 + 0.75 * tone);\n\n // Depth: darken the cell interior, then draw the ridge between cells.\n col = col * mix(1.0, smoothstep(0.0, 0.55, cell.f1), params.c);\n\n let ridge = 1.0 - smoothstep(0.0, params.b, edge);\n col = col + st_palette(1.0) * ridge * params.intensity;\n\n return st_post(col, uv);\n}\n",
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=sources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sources.js","sourceRoot":"","sources":["../../src/generated/sources.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,4EAA4E;AAC5E,oEAAoE;AAEpE,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,QAAQ,EAAE,68OAA68O;IACv9O,OAAO,EAAE,i/OAAi/O;IAC1/O,UAAU,EAAE,+iPAA+iP;IAC3jP,QAAQ,EAAE,40OAA40O;IACt1O,YAAY,EAAE,yrOAAyrO;IACvsO,UAAU,EAAE,4lOAA4lO;IACxmO,OAAO,EAAE,2+OAA2+O;IACp/O,cAAc,EAAE,88NAA88N;IAC99N,cAAc,EAAE,s8PAAs8P;IACt9P,eAAe,EAAE,u8PAAu8P;IACx9P,WAAW,EAAE,kwPAAkwP;IAC/wP,QAAQ,EAAE,suNAAsuN;IAChvN,QAAQ,EAAE,mpOAAmpO;IAC7pO,MAAM,EAAE,27NAA27N;IACn8N,WAAW,EAAE,0qQAA0qQ;IACvrQ,SAAS,EAAE,g/NAAg/N;IAC3/N,QAAQ,EAAE,q7RAAq7R;IAC/7R,SAAS,EAAE,+uOAA+uO;CAClvO,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { shaderRegistry, shaderNames, getShader, isShaderName } from "./registry.js";
|
|
2
|
+
export type { ShaderRegistry, ShaderName, ShaderParams, ControlName, } from "./registry.js";
|
|
3
|
+
export { defineShader, defineShadertownConfig } from "./config.js";
|
|
4
|
+
export type { ShaderConfig, ShadertownConfig } from "./config.js";
|
|
5
|
+
export { defaultParams, resolveUniforms, parseColor, formatColor, encodeParams, decodeParams, } from "./params.js";
|
|
6
|
+
export { createShaderRenderer } from "./renderer.js";
|
|
7
|
+
export type { ShaderRenderer, ShaderRendererOptions, RendererStatus } from "./renderer.js";
|
|
8
|
+
export { getStage, isWebGpuSupported } from "./stage.js";
|
|
9
|
+
export { diffParams, toConfigSource, toReactSource, toVanillaSource, toExampleSource, toReadme, toLicense, toWgslSource, toInstallCommand, } from "./export.js";
|
|
10
|
+
export type { LicenceHolder } from "./export.js";
|
|
11
|
+
export { shaderSources } from "./generated/sources.js";
|
|
12
|
+
export type { ColorControl, ColorSlot, NumericControl, ParamSlot, PlayMode, QualityOptions, ResolvedUniforms, ShaderCost, ShaderDefinition, ShaderFamily, } from "./types.js";
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACrF,YAAY,EACV,cAAc,EACd,UAAU,EACV,YAAY,EACZ,WAAW,GACZ,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACnE,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAElE,OAAO,EACL,aAAa,EACb,eAAe,EACf,UAAU,EACV,WAAW,EACX,YAAY,EACZ,YAAY,GACb,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,YAAY,EAAE,cAAc,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE3F,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEzD,OAAO,EACL,UAAU,EACV,cAAc,EACd,aAAa,EACb,eAAe,EACf,eAAe,EACf,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEvD,YAAY,EACV,YAAY,EACZ,SAAS,EACT,cAAc,EACd,SAAS,EACT,QAAQ,EACR,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,gBAAgB,EAChB,YAAY,GACb,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { shaderRegistry, shaderNames, getShader, isShaderName } from "./registry.js";
|
|
2
|
+
export { defineShader, defineShadertownConfig } from "./config.js";
|
|
3
|
+
export { defaultParams, resolveUniforms, parseColor, formatColor, encodeParams, decodeParams, } from "./params.js";
|
|
4
|
+
export { createShaderRenderer } from "./renderer.js";
|
|
5
|
+
export { getStage, isWebGpuSupported } from "./stage.js";
|
|
6
|
+
export { diffParams, toConfigSource, toReactSource, toVanillaSource, toExampleSource, toReadme, toLicense, toWgslSource, toInstallCommand, } from "./export.js";
|
|
7
|
+
export { shaderSources } from "./generated/sources.js";
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAQrF,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAGnE,OAAO,EACL,aAAa,EACb,eAAe,EACf,UAAU,EACV,WAAW,EACX,YAAY,EACZ,YAAY,GACb,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAGrD,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEzD,OAAO,EACL,UAAU,EACV,cAAc,EACd,aAAa,EACb,eAAe,EACf,eAAe,EACf,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC"}
|
package/dist/params.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type ShaderName, type ShaderParams } from "./registry.js";
|
|
2
|
+
import type { ResolvedUniforms } from "./types.js";
|
|
3
|
+
/** `#rgb`, `#rrggbb` and `#rrggbbaa` all parse. Values stay in sRGB. */
|
|
4
|
+
export declare function parseColor(hex: string): [number, number, number, number];
|
|
5
|
+
export declare function formatColor(rgba: readonly number[]): string;
|
|
6
|
+
/** Every control at its registry default, as a plain params object. */
|
|
7
|
+
export declare function defaultParams<K extends ShaderName>(name: K): Required<ShaderParams<K>>;
|
|
8
|
+
/**
|
|
9
|
+
* Maps a shader's named controls onto the shared uniform block. Unknown keys
|
|
10
|
+
* are ignored rather than thrown on, so a config written for one shader
|
|
11
|
+
* degrades gracefully when pointed at another.
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveUniforms<K extends ShaderName>(name: K, params: ShaderParams<K> | undefined, frame: {
|
|
14
|
+
resolution: [number, number];
|
|
15
|
+
pointer: [number, number];
|
|
16
|
+
time: number;
|
|
17
|
+
}): ResolvedUniforms;
|
|
18
|
+
/**
|
|
19
|
+
* Serialises the params that differ from the registry defaults into a compact
|
|
20
|
+
* query string, so a tuned shader is a shareable URL.
|
|
21
|
+
*/
|
|
22
|
+
export declare function encodeParams<K extends ShaderName>(name: K, params: ShaderParams<K>): string;
|
|
23
|
+
export declare function decodeParams<K extends ShaderName>(name: K, search: string): ShaderParams<K>;
|
|
24
|
+
//# sourceMappingURL=params.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"params.d.ts","sourceRoot":"","sources":["../src/params.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AACnF,OAAO,KAAK,EAAkB,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnE,wEAAwE;AACxE,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAkBxE;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAM3D;AAED,uEAAuE;AACvE,wBAAgB,aAAa,CAAC,CAAC,SAAS,UAAU,EAAE,IAAI,EAAE,CAAC,GAAG,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAUtF;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,UAAU,EAClD,IAAI,EAAE,CAAC,EACP,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,EACnC,KAAK,EAAE;IAAE,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC/E,gBAAgB,CA+BlB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,UAAU,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,MAAM,CAQ3F;AAED,wBAAgB,YAAY,CAAC,CAAC,SAAS,UAAU,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,CAe3F"}
|
package/dist/params.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { shaderRegistry } from "./registry.js";
|
|
2
|
+
/** `#rgb`, `#rrggbb` and `#rrggbbaa` all parse. Values stay in sRGB. */
|
|
3
|
+
export function parseColor(hex) {
|
|
4
|
+
let value = hex.trim().replace(/^#/, "");
|
|
5
|
+
if (value.length === 3 || value.length === 4) {
|
|
6
|
+
value = value
|
|
7
|
+
.split("")
|
|
8
|
+
.map((c) => c + c)
|
|
9
|
+
.join("");
|
|
10
|
+
}
|
|
11
|
+
if (value.length !== 6 && value.length !== 8) {
|
|
12
|
+
return [1, 1, 1, 1];
|
|
13
|
+
}
|
|
14
|
+
const int = Number.parseInt(value, 16);
|
|
15
|
+
const hasAlpha = value.length === 8;
|
|
16
|
+
const r = (int >>> (hasAlpha ? 24 : 16)) & 0xff;
|
|
17
|
+
const g = (int >>> (hasAlpha ? 16 : 8)) & 0xff;
|
|
18
|
+
const b = (int >>> (hasAlpha ? 8 : 0)) & 0xff;
|
|
19
|
+
const a = hasAlpha ? int & 0xff : 255;
|
|
20
|
+
return [r / 255, g / 255, b / 255, a / 255];
|
|
21
|
+
}
|
|
22
|
+
export function formatColor(rgba) {
|
|
23
|
+
const channel = (v) => Math.round(Math.min(1, Math.max(0, v)) * 255)
|
|
24
|
+
.toString(16)
|
|
25
|
+
.padStart(2, "0");
|
|
26
|
+
return `#${channel(rgba[0] ?? 0)}${channel(rgba[1] ?? 0)}${channel(rgba[2] ?? 0)}`;
|
|
27
|
+
}
|
|
28
|
+
/** Every control at its registry default, as a plain params object. */
|
|
29
|
+
export function defaultParams(name) {
|
|
30
|
+
const definition = shaderRegistry[name];
|
|
31
|
+
const params = {};
|
|
32
|
+
for (const [key, control] of Object.entries(definition.controls)) {
|
|
33
|
+
params[key] = control.default;
|
|
34
|
+
}
|
|
35
|
+
params.colorA = definition.colors.colorA.default;
|
|
36
|
+
params.colorB = definition.colors.colorB.default;
|
|
37
|
+
params.colorC = definition.colors.colorC.default;
|
|
38
|
+
return params;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Maps a shader's named controls onto the shared uniform block. Unknown keys
|
|
42
|
+
* are ignored rather than thrown on, so a config written for one shader
|
|
43
|
+
* degrades gracefully when pointed at another.
|
|
44
|
+
*/
|
|
45
|
+
export function resolveUniforms(name, params, frame) {
|
|
46
|
+
const definition = shaderRegistry[name];
|
|
47
|
+
const merged = { ...defaultParams(name), ...(params ?? {}) };
|
|
48
|
+
const uniforms = {
|
|
49
|
+
resolution: frame.resolution,
|
|
50
|
+
pointer: frame.pointer,
|
|
51
|
+
time: frame.time,
|
|
52
|
+
speed: 1,
|
|
53
|
+
scale: 1,
|
|
54
|
+
intensity: 1,
|
|
55
|
+
vibrancy: 1,
|
|
56
|
+
grain: 0,
|
|
57
|
+
vignette: 0,
|
|
58
|
+
warp: 0,
|
|
59
|
+
a: 0,
|
|
60
|
+
b: 0,
|
|
61
|
+
c: 0,
|
|
62
|
+
d: 0,
|
|
63
|
+
colorA: parseColor(String(merged.colorA ?? definition.colors.colorA.default)),
|
|
64
|
+
colorB: parseColor(String(merged.colorB ?? definition.colors.colorB.default)),
|
|
65
|
+
colorC: parseColor(String(merged.colorC ?? definition.colors.colorC.default)),
|
|
66
|
+
};
|
|
67
|
+
for (const [key, control] of Object.entries(definition.controls)) {
|
|
68
|
+
const raw = merged[key];
|
|
69
|
+
const value = typeof raw === "number" && Number.isFinite(raw) ? raw : control.default;
|
|
70
|
+
uniforms[control.slot] = Math.min(control.max, Math.max(control.min, value));
|
|
71
|
+
}
|
|
72
|
+
return uniforms;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Serialises the params that differ from the registry defaults into a compact
|
|
76
|
+
* query string, so a tuned shader is a shareable URL.
|
|
77
|
+
*/
|
|
78
|
+
export function encodeParams(name, params) {
|
|
79
|
+
const defaults = defaultParams(name);
|
|
80
|
+
const search = new URLSearchParams();
|
|
81
|
+
for (const [key, value] of Object.entries(params)) {
|
|
82
|
+
if (value === undefined || value === defaults[key])
|
|
83
|
+
continue;
|
|
84
|
+
search.set(key, typeof value === "number" ? String(Number(value.toFixed(4))) : String(value).replace("#", ""));
|
|
85
|
+
}
|
|
86
|
+
return search.toString();
|
|
87
|
+
}
|
|
88
|
+
export function decodeParams(name, search) {
|
|
89
|
+
const definition = shaderRegistry[name];
|
|
90
|
+
const query = new URLSearchParams(search);
|
|
91
|
+
const params = {};
|
|
92
|
+
for (const [key, value] of query.entries()) {
|
|
93
|
+
if (key === "colorA" || key === "colorB" || key === "colorC") {
|
|
94
|
+
params[key] = value.startsWith("#") ? value : `#${value}`;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (Object.hasOwn(definition.controls, key)) {
|
|
98
|
+
const parsed = Number(value);
|
|
99
|
+
if (Number.isFinite(parsed))
|
|
100
|
+
params[key] = parsed;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return params;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=params.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"params.js","sourceRoot":"","sources":["../src/params.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAsC,MAAM,eAAe,CAAC;AAGnF,wEAAwE;AACxE,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,IAAI,KAAK,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACzC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7C,KAAK,GAAG,KAAK;aACV,KAAK,CAAC,EAAE,CAAC;aACT,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;aACjB,IAAI,CAAC,EAAE,CAAC,CAAC;IACd,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7C,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACtB,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACvC,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC;IACpC,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;IAChD,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAC/C,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAC9C,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;IACtC,OAAO,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAuB;IACjD,MAAM,OAAO,GAAG,CAAC,CAAS,EAAE,EAAE,CAC5B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;SAC1C,QAAQ,CAAC,EAAE,CAAC;SACZ,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACtB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;AACrF,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,aAAa,CAAuB,IAAO;IACzD,MAAM,UAAU,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,MAAM,GAAoC,EAAE,CAAC;IACnD,KAAK,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjE,MAAM,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAChC,CAAC;IACD,MAAM,CAAC,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;IACjD,MAAM,CAAC,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;IACjD,MAAM,CAAC,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;IACjD,OAAO,MAAmC,CAAC;AAC7C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAC7B,IAAO,EACP,MAAmC,EACnC,KAAgF;IAEhF,MAAM,UAAU,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,EAAE,GAAG,aAAa,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,EAAqC,CAAC;IAEhG,MAAM,QAAQ,GAAqB;QACjC,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,KAAK,EAAE,CAAC;QACR,KAAK,EAAE,CAAC;QACR,SAAS,EAAE,CAAC;QACZ,QAAQ,EAAE,CAAC;QACX,KAAK,EAAE,CAAC;QACR,QAAQ,EAAE,CAAC;QACX,IAAI,EAAE,CAAC;QACP,CAAC,EAAE,CAAC;QACJ,CAAC,EAAE,CAAC;QACJ,CAAC,EAAE,CAAC;QACJ,CAAC,EAAE,CAAC;QACJ,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC7E,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC7E,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;KAC9E,CAAC;IAEF,KAAK,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,QAAQ,CAA+B,EAAE,CAAC;QAC/F,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QACxB,MAAM,KAAK,GAAG,OAAO,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;QACtF,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;IAC/E,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAuB,IAAO,EAAE,MAAuB;IACjF,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAoC,CAAC;IACxE,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;IACrC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAyC,CAAC,EAAE,CAAC;QACrF,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,QAAQ,CAAC,GAAG,CAAC;YAAE,SAAS;QAC7D,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;IACjH,CAAC;IACD,OAAO,MAAM,CAAC,QAAQ,EAAE,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,YAAY,CAAuB,IAAO,EAAE,MAAc;IACxE,MAAM,UAAU,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAoC,EAAE,CAAC;IACnD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;QAC3C,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC7D,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC;YAC1D,SAAS;QACX,CAAC;QACD,IAAI,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC;YAC5C,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;YAC7B,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAAE,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;QACpD,CAAC;IACH,CAAC;IACD,OAAO,MAAyB,CAAC;AACnC,CAAC"}
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type CSSProperties, type ReactNode } from "react";
|
|
2
|
+
import type { ShaderConfig } from "./config.js";
|
|
3
|
+
import type { ShaderName, ShaderParams } from "./registry.js";
|
|
4
|
+
import { type RendererStatus, type ShaderRenderer } from "./renderer.js";
|
|
5
|
+
import type { PlayMode, QualityOptions } from "./types.js";
|
|
6
|
+
export interface ShaderCanvasProps<K extends ShaderName = ShaderName> {
|
|
7
|
+
/** A preset from `defineShader`. Anything below overrides it. */
|
|
8
|
+
preset?: ShaderConfig<K>;
|
|
9
|
+
shader?: K;
|
|
10
|
+
params?: ShaderParams<K>;
|
|
11
|
+
quality?: QualityOptions;
|
|
12
|
+
/**
|
|
13
|
+
* When the shader is allowed to animate.
|
|
14
|
+
* `visible` (the default) animates only while on screen.
|
|
15
|
+
*/
|
|
16
|
+
play?: PlayMode;
|
|
17
|
+
className?: string;
|
|
18
|
+
style?: CSSProperties;
|
|
19
|
+
/** Rendered instead of the canvas when WebGPU is unavailable. */
|
|
20
|
+
fallback?: ReactNode;
|
|
21
|
+
onStatusChange?: (status: RendererStatus) => void;
|
|
22
|
+
/** Feeds the `pointer` uniform. Off by default — it costs a listener. */
|
|
23
|
+
trackPointer?: boolean;
|
|
24
|
+
/** Clock offset, so a paused card shows a frame worth looking at. */
|
|
25
|
+
startTime?: number;
|
|
26
|
+
"aria-hidden"?: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A canvas running one shadertown shader.
|
|
30
|
+
*
|
|
31
|
+
* The renderer is created once per mount; param changes are pushed through
|
|
32
|
+
* `setParams` rather than by remounting, so dragging a slider never recompiles
|
|
33
|
+
* a pipeline.
|
|
34
|
+
*/
|
|
35
|
+
export declare function ShaderCanvas<K extends ShaderName>({ preset, shader, params, quality, play, className, style, fallback, onStatusChange, trackPointer, startTime, "aria-hidden": ariaHidden, }: ShaderCanvasProps<K>): import("react").JSX.Element;
|
|
36
|
+
/**
|
|
37
|
+
* Imperative escape hatch: attach a renderer to a canvas you own.
|
|
38
|
+
* Returns the live renderer so you can drive it from animation code.
|
|
39
|
+
*/
|
|
40
|
+
export declare function useShaderRenderer<K extends ShaderName>(canvasRef: React.RefObject<HTMLCanvasElement | null>, config: ShaderConfig<K>): ShaderRenderer | null;
|
|
41
|
+
//# sourceMappingURL=react.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"AAEA,OAAO,EAOL,KAAK,aAAa,EAClB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAwB,KAAK,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/F,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE3D,MAAM,WAAW,iBAAiB,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU;IAClE,iEAAiE;IACjE,MAAM,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;IACzB,MAAM,CAAC,EAAE,CAAC,CAAC;IACX,MAAM,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;IACzB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB;;;OAGG;IACH,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,iEAAiE;IACjE,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;IAClD,yEAAyE;IACzE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,qEAAqE;IACrE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAgBD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,UAAU,EAAE,EACjD,MAAM,EACN,MAAM,EACN,MAAM,EACN,OAAO,EACP,IAAI,EACJ,SAAS,EACT,KAAK,EACL,QAAQ,EACR,cAAc,EACd,YAAoB,EACpB,SAAS,EACT,aAAa,EAAE,UAAiB,GACjC,EAAE,iBAAiB,CAAC,CAAC,CAAC,+BA2GtB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,UAAU,EACpD,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,iBAAiB,GAAG,IAAI,CAAC,EACpD,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,GACtB,cAAc,GAAG,IAAI,CAqBvB"}
|