

Creating Realistic Water Fill Effects in After Effects with Expressions
Introduction
Creating realistic liquid animations can be challenging, especially when you need dynamic, adjustable water filling effects. While there are plugins available for this purpose, you can actually achieve a professional-looking water fill effect using expressions directly in After Effects.
In this tutorial, I’ll share a powerful expression that creates a smooth, realistic water filling animation with an animated sine-wave surface. The best part? It’s fully customizable with simple slider controls.
What You’ll Create
This expression creates a water filling effect that:
- Features a realistic undulating surface with sine waves
- Animates smoothly with adjustable wave speed
- Allows precise control over the fill percentage
- Works in any container shape and size
- Requires no third-party plugins

Setup Instructions
Step 1: Create Your Shape Layer
- Create a new composition in After Effects
- Add a new Shape Layer to your composition
- Add a Fill effect to the shape layer with your desired water color (blues and teals work well)
Step 2: Add Expression Controls
Add these Expression Controls to your shape layer:
- Fill Percentage (Slider Control, 0-100)
- Wave Amplitude (Slider Control, suggested range: 0-100)
- Wave Frequency (Slider Control, suggested range: 0.01-0.1)
- Phase Speed (Slider Control, suggested range: 0-5)
- Container Width (Slider Control, set to your desired width in pixels)
- Container Height (Slider Control, set to your desired height in pixels)
- Number of Wave Points (Slider Control, suggested range: 10-50)
- Bezier Smoothness (Slider Control, suggested range: 0.2-1)
Step 3: Create the Path and Apply the Expression
- Add a Path to the Contents > Shape section of your shape layer
- Select the Path property
- Alt+click (or Option+click on Mac) on the stopwatch icon to open the expression editor
- Copy and paste the following expression:
// Retrieve Expression Control values
var fillPct = effect("Fill Percentage")("Slider") / 100; // normalized water level (0-1)
var amp = effect("Wave Amplitude")("Slider"); // wave amplitude (pixels)
var freq = effect("Wave Frequency")("Slider"); // wave frequency
var phaseSpeed = effect("Phase Speed")("Slider"); // determines the phase animation speed
var containerW = effect("Container Width")("Slider"); // container width in pixels
var containerH = effect("Container Height")("Slider"); // container height in pixels
var numPoints = Math.round(effect("Number of Wave Points")("Slider")); // number of points along top edge
var bezSmooth = effect("Bezier Smoothness")("Slider"); // factor for smoothing bezier handles
// Automatically animate the phase based on time and phaseSpeed
var phase = time * phaseSpeed;
// Calculate the base water level within the container
var yBase = containerH * (1 - fillPct);
// Create an array to hold the points (vertices) of the top edge
var pts = [];
// Generate the top edge points with sine wave modulation
for (var i = 0; i <= numPoints; i++){
var x = containerW * i / numPoints;
var waveOffset = amp * Math.sin(freq * x + phase);
var y = yBase + waveOffset;
pts.push([x, y]);
}
// Compute smooth Bezier handles for a smooth curve based on neighboring point differences
var inTans = [];
var outTans = [];
for (var i = 0; i < pts.length; i++){
var tangent;
if (i == 0 || i == pts.length - 1) {
// Endpoints: assign no tangent for minimal distortion
tangent = [0,0];
} else {
var prev = pts[i-1];
var next = pts[i+1];
// Compute the tangent as the difference between next and previous points,
// scaled by a factor and adjusted by bezSmooth.
tangent = [ (next[0] - prev[0]) * bezSmooth / 4, (next[1] - prev[1]) * bezSmooth / 4 ];
}
// Store the computed tangent for both in and out (opposite directions)
outTans.push(tangent);
inTans.push([-tangent[0], -tangent[1]]);
}
// Build the complete vertex list for the path:
// Start with the top edge points defined by pts.
var vertices = pts.slice();
// Append the bottom right and bottom left corners to close the container shape.
vertices.push([containerW, containerH]);
vertices.push([0, containerH]);
// For the newly added bottom corners, assign zero tangents.
inTans.push([0, 0]);
inTans.push([0, 0]);
outTans.push([0, 0]);
outTans.push([0, 0]);
// Close the shape.
var isClosed = true;
// Create and return the path without altering the shape layer's size.
createPath(vertices, inTans, outTans, isClosed);
Step 4: Configure Your Water Effect
- Set your Container Width and Container Height to match your desired container dimensions
- Adjust Fill Percentage to set the water level (0% = empty, 100% = full)
- Fine-tune the wave parameters:
- Higher Wave Amplitude = taller waves
- Higher Wave Frequency = more waves horizontally
- Higher Phase Speed = faster wave animation
- Higher Number of Wave Points = smoother waves (but more calculation-intensive)
- Adjust Bezier Smoothness to fine-tune the wave smoothness
Understanding the Parameters
Fill Percentage
Controls how full your container appears. At 0%, the water is at the bottom of the container; at 100%, it fills the entire container.
Wave Amplitude
Determines the height of the waves in pixels. Higher values create more dramatic waves, while lower values create a calmer surface.
Wave Frequency
Controls how many waves appear across the width of your container. Higher values create more tightly packed waves.
Phase Speed
Controls how quickly the waves animate horizontally. Higher values create faster-moving waves, while a value of 0 creates static waves.
Container Width & Height
These should match the dimensions of your desired water container in pixels.
Number of Wave Points
Determines how many points are used to create the wave curve. More points result in a smoother curve but require more processing power.
Bezier Smoothness
Fine-tunes the smoothness of the wave curves. Higher values create more rounded waves, while lower values create sharper transitions.
How It Works: Technical Explanation
The expression creates a dynamically generated path that simulates water with a wavy surface. Here’s the breakdown of what happens:
- Wave Generation: The expression creates a series of points along the top edge of the container using a sine wave function. The sine wave’s amplitude, frequency, and phase determine the wave’s appearance.
- Bezier Smoothing: To create a smooth, natural-looking wave rather than a jagged line, the expression calculates appropriate Bezier curve handles between each point. This is what gives the waves their fluid appearance.
- Path Completion: The expression completes the shape by adding points for the bottom corners of the container and closing the path.
- Animation: The phase of the sine wave is continuously updated based on the current time and the Phase Speed parameter, creating the illusion of waves moving across the surface.
Creative Use Cases
1. Liquid Fill Animations
Create animations where a container fills with liquid over time by animating the Fill Percentage parameter.
2. Interactive Water Tanks
Combine with other expressions or controls to create interactive water tanks that respond to user input or other animations.
3. Underwater Scenes
Use as a background element for underwater scenes, with the top edge representing the water surface viewed from below.
4. Potion Bottles or Science Equipment
Perfect for creating animated magical potions or laboratory equipment in motion graphics.
5. Progress Indicators
Use as a creative alternative to standard progress bars for loading screens or progress indicators.
Customization Tips
Adding Color Gradients
Replace the solid fill with a gradient fill to create more realistic water with depth variations.
// Add this to your shape layer:
// 1. Add a Gradient Fill instead of a solid fill
// 2. Set the gradient colors from light blue at the top to darker blue at the bottom
Creating Foam or Bubbles
Add a Stroke effect to the top edge of your water to simulate foam:
// 1. Add a Stroke effect to your shape
// 2. Set it to a white or light color
// 3. Use a small stroke width (1-3px)
// 4. Set the Stroke options to "Along Path"
Adding Reflections
Duplicate your water layer, flip it vertically, and adjust opacity and blending modes to create reflections:
// 1. Duplicate the water layer
// 2. Apply Transform effect with Scale Y = -100%
// 3. Position below the original water
// 4. Reduce opacity to 30-50%
// 5. Try Screen or Add blending mode
Troubleshooting
The Water Doesn’t Fill the Container Properly
- Double-check that your Container Width and Height values match your intended container size
- Ensure Fill Percentage is set appropriately
The Animation Looks Choppy
- Reduce the Number of Wave Points to improve performance
- Check that your Phase Speed isn’t too high
The Waves Look Unnatural
- Adjust the Wave Amplitude and Frequency to more realistic values
- Fine-tune the Bezier Smoothness for better curve transitions
The Expression Gives an Error
- Verify that all the required Expression Controls are added to the layer
- Check that the names of the controls exactly match those used in the expression
Conclusion
This water fill expression is a powerful tool for creating realistic liquid animations in After Effects without the need for third-party plugins. By understanding and tweaking the parameters, you can create a wide range of water effects for your projects.
The expression works by generating a dynamic path with a sine-wave top edge, complete with smooth Bezier curve transitions. This creates the illusion of a fluid water surface that can be animated to fill a container or simply undulate in place.
Experiment with different parameter combinations to achieve various water effects—from calm, subtle waves to more aggressive, choppy surfaces. With a little creativity, you can adapt this basic technique to create many different liquid animations.
Pro Tip: For the most realistic water effect, combine this expression with a subtle noise or turbulence displacement effect applied to the entire shape layer. This adds secondary motion that simulates the chaotic nature of real water.
Feel free to download example project file:
Download “Water Filling” Water-Filling-Expression.zip – Downloaded 2 times – 7.42 KB