diff --git a/apps/lab/app/components/pixel-noise-shaders-sandbox/ThePixelNoiseShadersSandbox.vue b/apps/lab/app/components/pixel-noise-shaders-sandbox/ThePixelNoiseShadersSandbox.vue new file mode 100644 index 000000000..f0b88d60c --- /dev/null +++ b/apps/lab/app/components/pixel-noise-shaders-sandbox/ThePixelNoiseShadersSandbox.vue @@ -0,0 +1,217 @@ + + + diff --git a/apps/lab/app/components/pixel-noise-shaders-sandbox/index.global.vue b/apps/lab/app/components/pixel-noise-shaders-sandbox/index.global.vue new file mode 100644 index 000000000..bcb782016 --- /dev/null +++ b/apps/lab/app/components/pixel-noise-shaders-sandbox/index.global.vue @@ -0,0 +1,25 @@ + + + diff --git a/apps/lab/app/components/pixel-noise-shaders-sandbox/shaders/fragment.glsl b/apps/lab/app/components/pixel-noise-shaders-sandbox/shaders/fragment.glsl new file mode 100644 index 000000000..ffd721a80 --- /dev/null +++ b/apps/lab/app/components/pixel-noise-shaders-sandbox/shaders/fragment.glsl @@ -0,0 +1,10 @@ +precision highp float; + +varying vec3 vColor; + +void main() { + vec2 pc = gl_PointCoord; + float edge = min(min(pc.x, 1.0 - pc.x), min(pc.y, 1.0 - pc.y)); + float alpha = smoothstep(0.0, 0.15, edge); + gl_FragColor = vec4(vColor, alpha); +} diff --git a/apps/lab/app/components/pixel-noise-shaders-sandbox/shaders/vertex.glsl b/apps/lab/app/components/pixel-noise-shaders-sandbox/shaders/vertex.glsl new file mode 100644 index 000000000..ebe8b63e1 --- /dev/null +++ b/apps/lab/app/components/pixel-noise-shaders-sandbox/shaders/vertex.glsl @@ -0,0 +1,103 @@ +uniform vec2 uResolution; +uniform float uTime; +uniform sampler2D uDisplacementTexture; +uniform vec3 uColorPrimary; +uniform vec3 uColorSecondary; +uniform float uZoom; +uniform float uSizeContrast; +uniform float uSpeed; +uniform float uDisplacementStrength; +uniform float uMinCellSize; +uniform float uMaxCellSize; + +attribute float aIntensity; +attribute float aAngle; + +varying vec3 vColor; + +// Simplex noise (Ashima Arts) +vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } +vec2 mod289(vec2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } +vec3 permute(vec3 x) { return mod289(((x * 34.0) + 10.0) * x); } + +float snoise(vec2 v) { + const vec4 C = vec4( + 0.211324865405187, + 0.366025403784439, + -0.577350269189626, + 0.024390243902439 + ); + vec2 i = floor(v + dot(v, C.yy)); + vec2 x0 = v - i + dot(i, C.xx); + vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0); + vec4 x12 = x0.xyxy + C.xxzz; + x12.xy -= i1; + i = mod289(i); + vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0)) + i.x + vec3(0.0, i1.x, 1.0)); + vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), 0.0); + m = m * m; + m = m * m; + vec3 x = 2.0 * fract(p * C.www) - 1.0; + vec3 h = abs(x) - 0.5; + vec3 ox = floor(x + 0.5); + vec3 a0 = x - ox; + m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h); + vec3 g; + g.x = a0.x * x0.x + h.x * x0.y; + g.yz = a0.yz * x12.xz + h.yz * x12.yw; + return 130.0 * dot(m, g); +} + +float snoise01(vec2 v) { + return (1.0 + snoise(v)) * 0.5; +} + +float noise2d(vec2 st, float t) { + return snoise01(vec2(st.x + t * 0.005, st.y - t * 0.008)); +} + +// Domain warping — iquilezles.org/articles/warp +float pattern(vec2 p, float t) { + vec2 q = vec2( + noise2d(p + vec2(0.0, 0.0), t), + noise2d(p + vec2(5.2, 1.3), t) + ); + vec2 r = vec2( + noise2d(p + 4.0 * q + vec2(1.7, 9.2), t), + noise2d(p + 4.0 * q + vec2(8.3, 2.8), t) + ); + return noise2d(p + 1.0 * r, t); +} + +void main() { + vec3 newPosition = position; + float t = uTime * uSpeed; + + // --- Fluid wave intensity: domain-warped noise drives point size across the full plane --- + float n = pattern(uv * uZoom + t * 0.015, t); + float waveIntensity = smoothstep(0.1, 0.85, n); + + // --- Color --- + float noiseBlend = snoise01(uv * 3.0 + t * 0.005); + vColor = mix(uColorSecondary, uColorPrimary, waveIntensity * noiseBlend); + vColor = pow(vColor, vec3(2.0)); + + // --- Cursor displacement (subtle) --- + float displacementIntensity = texture2D(uDisplacementTexture, uv).r; + displacementIntensity = smoothstep(0.1, 0.9, displacementIntensity); + vec3 displacement = vec3(cos(aAngle), sin(aAngle), 0.0); + displacement *= displacementIntensity * uDisplacementStrength * aIntensity; + newPosition += displacement; + + // --- Final position --- + vec4 modelPosition = modelMatrix * vec4(newPosition, 1.0); + vec4 viewPosition = viewMatrix * modelPosition; + vec4 projectedPosition = projectionMatrix * viewPosition; + gl_Position = projectedPosition; + + // --- Point size driven by fluid wave --- + float sizeWave = pow(waveIntensity, uSizeContrast); + gl_PointSize = 0.1 * uResolution.y * sizeWave; + gl_PointSize *= (1.0 / -viewPosition.z); + gl_PointSize = clamp(gl_PointSize, uMinCellSize, uMaxCellSize); +} diff --git a/apps/lab/app/components/pixel-plasma-shader/ThePixelPlasmaShader.vue b/apps/lab/app/components/pixel-plasma-shader/ThePixelPlasmaShader.vue new file mode 100644 index 000000000..5e6f6101d --- /dev/null +++ b/apps/lab/app/components/pixel-plasma-shader/ThePixelPlasmaShader.vue @@ -0,0 +1,166 @@ + + + diff --git a/apps/lab/app/components/pixel-plasma-shader/index.global.vue b/apps/lab/app/components/pixel-plasma-shader/index.global.vue new file mode 100644 index 000000000..2cee9c3c6 --- /dev/null +++ b/apps/lab/app/components/pixel-plasma-shader/index.global.vue @@ -0,0 +1,19 @@ + + + diff --git a/apps/lab/app/components/pixel-plasma-shader/shaders/fragment.glsl b/apps/lab/app/components/pixel-plasma-shader/shaders/fragment.glsl new file mode 100644 index 000000000..4ca41bd71 --- /dev/null +++ b/apps/lab/app/components/pixel-plasma-shader/shaders/fragment.glsl @@ -0,0 +1,16 @@ +precision highp float; + +varying vec3 vColor; +varying float vMask; + +void main() { + vec2 pc = gl_PointCoord; // [0, 1] range + + // Hard square clip using Chebyshev distance + vec2 centered = abs(pc - 0.5); + float squareDist = max(centered.x, centered.y); + if (squareDist >= 0.5) discard; + + float alpha = vMask; + gl_FragColor = vec4(vColor, alpha); +} diff --git a/apps/lab/app/components/pixel-plasma-shader/shaders/vertex.glsl b/apps/lab/app/components/pixel-plasma-shader/shaders/vertex.glsl new file mode 100644 index 000000000..4ba0d63ba --- /dev/null +++ b/apps/lab/app/components/pixel-plasma-shader/shaders/vertex.glsl @@ -0,0 +1,111 @@ +#define PI 3.14159265358979 + +uniform vec2 uResolution; +uniform float uTime; +uniform sampler2D uDisplacementTexture; +uniform vec3 uColorPrimary; +uniform vec3 uColorSecondary; +uniform float uZoom; +uniform float uSizeContrast; +uniform float uSpeed; +uniform float uSineFrequency; +uniform float uSineSpeed; +uniform float uSineAmplitude; +uniform float uGridCellSize; +uniform float uDisplacementStrength; +uniform float uMinCellSize; +uniform float uMaxCellSize; + +attribute float aIntensity; +attribute float aAngle; + +varying vec3 vColor; +varying float vMask; + +// Ashima Arts simplex noise +vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } +vec2 mod289(vec2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } +vec3 permute(vec3 x) { return mod289(((x * 34.0) + 10.0) * x); } + +float snoise(vec2 v) { + const vec4 C = vec4( + 0.211324865405187, + 0.366025403784439, + -0.577350269189626, + 0.024390243902439 + ); + vec2 i = floor(v + dot(v, C.yy)); + vec2 x0 = v - i + dot(i, C.xx); + vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0); + vec4 x12 = x0.xyxy + C.xxzz; + x12.xy -= i1; + i = mod289(i); + vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0)) + i.x + vec3(0.0, i1.x, 1.0)); + vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), 0.0); + m = m * m; + m = m * m; + vec3 x = 2.0 * fract(p * C.www) - 1.0; + vec3 h = abs(x) - 0.5; + vec3 ox = floor(x + 0.5); + vec3 a0 = x - ox; + m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h); + vec3 g; + g.x = a0.x * x0.x + h.x * x0.y; + g.yz = a0.yz * x12.xz + h.yz * x12.yw; + return 130.0 * dot(m, g); +} + +// FBM: 4 octaves of simplex noise +float fbm(vec2 p) { + float v = 0.0, amp = 0.5, freq = 1.0, normFactor = 0.0; + for (int i = 0; i < 4; i++) { + v += (snoise(p * freq) * 0.5 + 0.5) * amp; + normFactor += amp; + amp *= 0.5; + freq *= 2.0; + } + return v / normFactor; +} + +void main() { + vec3 newPosition = position; + float t = uTime * uSpeed; + + // --- Domain-warped FBM → waveIntensity --- + vec2 q = vec2( + fbm(uv * 2.0 + t * 0.02), + fbm(uv * 2.0 + vec2(5.2, 1.3) + t * 0.02) + ); + vec2 r = vec2( + fbm((uv + 2.0 * q) * 2.0 + vec2(1.7, 9.2)), + fbm((uv + 2.0 * q) * 2.0 + vec2(8.3, 2.8)) + ); + float waveIntensity = smoothstep(0.05, 0.95, fbm((uv + r) * 2.0 * uZoom)); + + // --- Color --- + float noiseBlend = snoise(uv * 3.0 + t * 0.005) * 0.5 + 0.5; + vColor = mix(uColorSecondary, uColorPrimary, waveIntensity * noiseBlend); + + // --- Scrolling sine mask --- + float sineEdge = uSineAmplitude * sin(uv.y * uSineFrequency * PI + uTime * uSineSpeed); + float maskX = uv.x - (0.45 + sineEdge); + vMask = smoothstep(-0.05, 0.08, maskX); + + // --- Cursor displacement (subtle) --- + float displacementIntensity = texture2D(uDisplacementTexture, uv).r; + displacementIntensity = smoothstep(0.1, 0.9, displacementIntensity); + vec3 displacement = vec3(cos(aAngle), sin(aAngle), 0.0); + displacement *= displacementIntensity * uDisplacementStrength * aIntensity; + newPosition += displacement; + + // --- Final position + point size --- + vec4 mvPosition = modelViewMatrix * vec4(newPosition, 1.0); + gl_Position = projectionMatrix * mvPosition; + + // Size that exactly fills one grid cell (no overlap) + float baseSize = uGridCellSize * projectionMatrix[1][1] * uResolution.y * 0.5 / (-mvPosition.z); + + float sizeWave = pow(waveIntensity, uSizeContrast); + float sizeMask = mix(0.05, 1.0, vMask); + gl_PointSize = clamp(baseSize * sizeWave * sizeMask, uMinCellSize, uMaxCellSize); +} diff --git a/apps/lab/content/experiments/pixel-noise-shaders-sandbox.md b/apps/lab/content/experiments/pixel-noise-shaders-sandbox.md new file mode 100644 index 000000000..525079742 --- /dev/null +++ b/apps/lab/content/experiments/pixel-noise-shaders-sandbox.md @@ -0,0 +1,9 @@ +--- +title: Pixel Noise Shaders Sandbox +author: alvarosabu +description: A sandbox for experimenting with pixel noise shaders +thumbnail: /experiments/pixel-noise-shaders-sandbox.png +tags: ['shaders', 'noise', 'glsl'] +date: 2026-03-25 +lastUpdated: 2026-03-25 +--- diff --git a/apps/lab/content/experiments/pixel-plasma-shader.md b/apps/lab/content/experiments/pixel-plasma-shader.md new file mode 100644 index 000000000..601e1e790 --- /dev/null +++ b/apps/lab/content/experiments/pixel-plasma-shader.md @@ -0,0 +1,9 @@ +--- +title: Pixel Plasma Shader +author: alvarosabu +description: A sandbox for experimenting with pixel plasma shaders +thumbnail: /experiments/pixel-plasma-shader.png +tags: ['shaders', 'plasma', 'glsl'] +date: 2026-03-31 +lastUpdated: 2026-03-31 +--- diff --git a/apps/lab/public/experiments/pixel-noise-shaders-sandbox.png b/apps/lab/public/experiments/pixel-noise-shaders-sandbox.png new file mode 100644 index 000000000..d8e09dfc4 Binary files /dev/null and b/apps/lab/public/experiments/pixel-noise-shaders-sandbox.png differ diff --git a/apps/lab/public/experiments/pixel-plasma-shader.png b/apps/lab/public/experiments/pixel-plasma-shader.png new file mode 100644 index 000000000..7fad76a3e Binary files /dev/null and b/apps/lab/public/experiments/pixel-plasma-shader.png differ diff --git a/apps/playground/src/pages/leches/advanced/folders-parent-child/TheChild.vue b/apps/playground/src/pages/leches/advanced/folders-parent-child/TheChild.vue index 52f38d9f3..926320d4a 100644 --- a/apps/playground/src/pages/leches/advanced/folders-parent-child/TheChild.vue +++ b/apps/playground/src/pages/leches/advanced/folders-parent-child/TheChild.vue @@ -60,6 +60,11 @@ watch(number, (value) => { // eslint-disable-next-line no-console console.log('number', value) }) + +watch(paletteControls, (value) => { + // eslint-disable-next-line no-console + console.log('paletteControls', value) +})