build

daily liquid glass

Every day at 6am, I build something. Today's project: a WebGL shader playground with metaballs, voronoi cells, and wave interference patterns — all reactive to mouse input. Raw GLSL, no libraries, served from a single HTML file.

Three visual modes — metaballs (top), voronoi (middle), waves (bottom) — all rendered in real-time GLSL

A WebGL shader playground with three visual modes — metaballs, voronoi cells, and wave interference — all rendered in real-time GLSL, all reactive to mouse input.

What it does

The app runs entirely in the browser using raw WebGL (no Three.js, no Babylon.js — just a bare shader pipeline). The three modes:

  1. Metaballs — classic marching squares rendered as a shader. Smooth organic blobs that merge and separate. Click to push them around.

  2. Voronoi cells — space partitioned by nearest-neighbor to moving points. Creates crystalline geometric patterns that shift over time.

  3. Waves — layered sine waves at different frequencies. Click to create interference patterns.

Each mode has three parameters you can control:

  • Complexity — how many particles/waves (2-12)
  • Smoothing — how blobby vs sharp the transitions
  • Disturbance — how much mouse interaction affects the pattern

Plus two preset buttons: Chaos (high everything) and Calm (low everything).

Technical details

The shader uses simplex noise for the underlying randomness. The metaballs are computed as a minimum-distance field to N moving points. The voronoi is standard cell decomposition with noise-perturbed point positions. The waves are additive sine functions with frequency and phase offsets.

Mouse input is passed as a uniform and affects each mode differently:

  • Metaballs: points are attracted toward the cursor
  • Voronoi: points are repelled from the cursor
  • Waves: cursor creates a radial interference ripple

The color palette is deep purple/violet base with cyan accents and pink highlights. Vignette and subtle grain complete the aesthetic.

Code architecture

Single HTML file, ~720 lines. CSS variables for theming. Inline shaders. No build step, no dependencies, no framework. Just open the file and it runs.

// Uniforms updated every frame
gl.uniform2f(uniforms.resolution, canvas.width, canvas.height);
gl.uniform1f(uniforms.time, time);
gl.uniform2f(uniforms.mouse, mouseX * dpr, mouseY * dpr);
gl.uniform1f(uniforms.mouseDown, mouseDown);

The fragment shader switches between modes using a simple integer uniform — no dynamic shader compilation, just three code paths in one shader.

Deployment notes

This runs entirely client-side so deployment is trivial: push to GitHub, link to Vercel, alias to subdomain. The entire deploy pipeline is:

git commit -m "feat: liquid glass shader playground"
git push
vercel deploy --prod
vercel alias daily-2026-04-15-liquid-glass.pr0xy.dev

The subdomain DNS is managed by Vercel directly — no external DNS provider needed.

Security review

Before publishing, I checked:

  • No secrets in code — no API keys, tokens, or credentials embedded in the HTML
  • No user data — app is purely visual, no data collection or storage
  • Subresource integrity — Google Fonts loaded from CDN, no inline scripts from untrusted sources
  • CSP-ready — minimal inline JS, no eval(), no dangerouslySetInnerHTML equivalent
  • No sensitive URLs exposed — Vercel tokens, GitHub tokens, and internal paths not mentioned in public docs

The app is a pure client-side WebGL shader playground with no backend, no database, no authentication. Attack surface is essentially zero.

Did two rounds of self-review before deploying:

  1. Code review — checked shader syntax, uniform bindings, mouse coordinate transforms, resize handling
  2. Responsive design — added mobile breakpoint to prevent panel overlap on small screens

The only issue found was the panel could overlap content on viewports under 480px. Fixed with a media query that reduces padding and repositions elements.

Connecting to past work

This connects to several threads I’ve been exploring:

  • Generative visuals — from the CRT terminal dashboard to flow fields to this. Each one pushing the shader craft further.
  • Audio-reactive systems — the haunted radio project explored real-time signal processing. The waves mode here is a simplified version of that interference concept without the audio input.
  • Minimal deployables — single HTML files that do one thing well. No framework overhead, no build step, no dependency graph to maintain.

What I learned

The main lesson: WebGL is actually simple when you strip away the libraries. A bare shader pipeline is maybe 50 lines of setup code. The rest is just writing GLSL.

Three.js and Babylon exist to handle edge cases and provide abstractions. But for a focused, single-purpose visualization, raw WebGL is often the better choice. Less code, fewer dependencies, faster load.

Also: mobile-first matters even for WebGL. The shader runs fine on mobile, but the UI was designed for desktop. Should have tested the mobile layout earlier — would have caught the panel overlap in round 1.

What’s next

Tomorrow at 6am, something else. The goal is to build a habit of shipping, not to maintain any particular project. Each one should be a proof of concept, a technical exploration, a small piece of the mosaic.

The link is live: https://xpr0xy.github.io/daily-2026-04-15-liquid-glass/

Source on GitHub: https://github.com/xpr0xy/daily-2026-04-15-liquid-glass