14.9 C
New York
Saturday, October 25, 2025

Dissecting a Wavy Shader: Sine, Refraction, and Serendipity



Each inventive studio has its bizarre inner rituals.

Ours? A problem we name “12 Pens in 12 Months” — one experiment every month, no guidelines, no purchasers, simply play.

Someplace between caffeine and chaos, one in all our devs made this — a wavy, hypnotic, jelly-like movement impact that immediately turned the group’s favourite. We didn’t plan to make something “severe”, however individuals began asking the way it works, and right here we’re — writing about it on Codrops!

The Idea

The concept began easy:

What if we might make movement that feels natural — not mechanical, not linear, however one thing that flows?

We needed that “liquid between two worlds” really feel — a movement that appears to breathe, stretch, and loosen up.

At its core, it’s a mixture of:

  • fragment shaders (to generate geometric wave-like cells)
  • math-driven distortion (sine waves, ripples, and refraction)
  • and requestAnimationFrame for clean, steady GPU updates.
perform draw(tms) {
  const t = tms * 0.001;

  // natural movement: a sine wave that lightly distorts the grid measurement over time
  const wave = Math.sin(p.x * 0.01 + p.y * 0.015 + t) * 0.25;
  const localCell = cell * (1.0 + wave * 0.2);

  // ripple impact triggered by person interplay (click on)
  float ripple = sin(R * 0.06 - dt * 6.0) * env;

  // mixing the glass-like refraction with the bottom picture
  vec3 col = combine(base, glass, inside);

  gl_FragColor = vec4(col, 1.0);
}

requestAnimationFrame(draw);

How It Works

The magic trick right here is in how we recalculate the wave path each body.

As a substitute of animating DOM or CSS properties, we rebuild each pixel dynamically within the shader — body by body.

Every cell behaves like a residing floor — pulsing, refracting, and rippling with time.

Think about drawing a line with a rubber band — every anchor level follows the one earlier than it, with a little bit of delay and overshoot. That delay provides us that deliciously gooey, alive motion.

For efficiency causes, we use:

  • requestAnimationFrame for clean updates.
  • GPU-driven math — sine, refraction, ripple computed instantly within the shader.
  • requestAnimationFrame to sync GPU frames with the browser’s refresh price.
// discover the closest cell heart based mostly on the chosen form
void nearestCenter(int form, vec2 p, float cell, out vec2 c, out vec2 lp) {
  if (form == 0) {
    vec2 qr  = hex_pixel_to_axial(p, cell);
    vec2 qrr = hex_axial_round(qr);
    c = hex_axial_to_pixel(qrr, cell);
    lp = p - c;
  } else {
    vec2 g = ground(p / cell + 0.5);
    c = g * cell;
    lp = p - c;
  }
}

Presets & Variations

As soon as the core system was operating, we couldn’t cease tweaking it.

As a substitute of mounted presets, we constructed a easy management panel — 4 sliders that management the shader parameters instantly:

  • Cell measurement (uCell) — density of the grid
  • Amplitude (uAmp) — power of refraction
  • Chromatic shift (uChrom) — quantity of shade separation
  • Pace (uSpeed) — how briskly the wave evolves

Altering simply one in all these immediately transforms the temper of the movement — from smooth and fluid to sharp and energetic.

Each body, the app reads the stay values from the sliders and sends them straight into the shader —

so any tiny change immediately ripples by means of the whole floor.

perform draw(tms) {
  const t = tms * 0.001;

  // stay parameters from the UI → shader
  gl.uniform1f(uCell,   parseFloat(cellInp.worth));
  gl.uniform1f(uAmp,    parseFloat(ampInp.worth));
  gl.uniform1f(uChrom,  parseFloat(chromInp.worth));
  gl.uniform1f(uSpeed,  parseFloat(speedInp.worth));
  gl.uniform1f(uTime,   t);

  // replace the video texture (if energetic)
  if (videoReady) {
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, videoEl);
  }

  // render + subsequent body
  gl.drawArrays(gl.TRIANGLES, 0, 6);
  requestAnimationFrame(draw);
}

Challenges Alongside the Method

After all, it wasn’t all clean crusing (pun meant).

At one level the wave went full chaos — spikes, glints, every part breaking up.

Seems, our smoothing logic was off by one pixel (actually).

One other humorous bug: when testing on high-refresh screens, the movement appeared too clean — prefer it misplaced its texture. So we had so as to add a contact of imperfection to convey again the “handmade” vibe.

Attempt It Your self

The entire setup is open and simple to remix — simply fork it and begin enjoying with the parameters.

Attempt altering:

  • the variety of factors
  • the easing curves
  • the colour gradients

We’d like to see what sort of waves you make — tag us or ship your remix!

Ultimate Ideas

Typically the most effective concepts come from not making an attempt too exhausting.

This one was alleged to be a fast inner experiment — nevertheless it was one thing oddly satisfying and visually wealthy.

We hope you get pleasure from it as a lot as we loved breaking (and fixing) it.

Made with ❤️ by Blacklead Studio as a part of our 12 Pens in 12 Months inventive problem.



Supply hyperlink

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles