Flutter Shaders

Water Ripple

A water ripple that emanates from the location of a user's tap.

Use the code below to add this shader to a Flutter app. If you’re not sure what to do with the provided code, check out our usage guide, or learn how shaders works.

/*
This shader is ported from the original Apple shader presented at WWDC 2024.
For more details, see the session here: https://developer.apple.com/videos/play/wwdc2024/10151/
Credit to Apple for the original implementation.
*/

#include <flutter/runtime_effect.glsl>

uniform vec2 iResolution;
uniform vec2 iMouse;
uniform float iTime;
uniform sampler2D iChannel0;

const float amplitude = 0.05; // Default amplitude of the ripple
const float frequency = 10.0; // Default frequency of the ripple
const float decay = 2.0; // Default decay rate of the ripple
const float speed = 1.0; // Default speed of the ripple

out vec4 fragColor;


void main()
{
    vec2 fragCoord = FlutterFragCoord().xy;

    // Normalize the coordinates
    vec2 uv = fragCoord / iResolution.xy;

    // Get the cursor position and normalize it
    vec2 origin = iMouse.xy / iResolution.xy;

    // Calculate the distance from the origin
    float distance = length(uv - origin);
    // Calculate the delay based on the distance
    float delay = distance / speed;

    // Adjust the time for the delay and clamp to 0
    float time = iTime - delay;
    time = max(0.0, time);

    // Calculate the ripple amount
    float rippleAmount = amplitude * sin(frequency * time) * exp(-decay * time);

    // Calculate the normalized direction vector
    vec2 n = normalize(uv - origin);

    // Calculate the new position by adding the ripple effect
    vec2 newPosition = uv + rippleAmount * n;

    // Sample the texture at the new position
    vec3 color = texture(iChannel0, newPosition).rgb;

    // Lighten or darken the color based on the ripple amount
    color += 0.1 * (rippleAmount / amplitude);

    // Set the fragment color
    fragColor = vec4(color, 1.0);
}