Making More with Waves: Trig Inspired SVG Artworks Part II
In this Post ...
In this follow-up to my previous post using trigonometry to animate SVG I'll show how I extended the math to enable more traveling wave effects. Then I'll demonstrate the technique by creating a gallery of SVG artworks using the SVG Creators Collaborative framework.
Introduction
I've been thinking a lot about waves lately...
More Animation Effects Inspired by Math: Making Waves
DOCUMENT THE travelling wave and water
To be completely transparent, this blog post started as an exploration of creating watery wave effects using pure SVG. My intent was to push the wave effect to it's limits in order to provide the right tooling and infrastructure for the SVG Creators Collaborative™. In attempting to create a compelling wave effect for my personal artwork I was having trouble reaching a completely satisfying solution.
Here's the problem. You can create some very nice wave effects using simple SMIL animation. For example, here's a nice watery effect achieved simply by animating a bezier path.
But what I was after was something more like a 'traveling wave' .
A traveling wave is a wave that propagates through a medium transferring energy and momentum without the net movement of the medium itself.
In other words, it's a wave that appears to move from one point to another (as opposed to a standing wave which remains in a fixed position).
The Painful Path of Discovery...
At the outset I assumed SVG -- with it's beautiful Bezier curves and SMIL animation -- would provide ready means to achieve my watery effect and I'd quickly be able to share the results in my blog. Sadly, the only thing quick about it was for me to find out getting a good traveling wave effect in SVG is not trivial.
My initial assumption was that I'd be able to gin up a quick SMIL animation if I could just get the right Bezier path going with the right handles. And that's what you see in the above simple wave animation. Here's the code.
<path id="init" fill="#088" > <animate id="SMIL_ANIMATION" attributeName="d" dur="3s" repeatCount="indefinite" values=" m 0,40 c 50,-12 94,-13 150,0 58,14 93,12 150,0 V 80 H 0 Z; m 0,40 c 48,16 98,15 150,0 53,-16 105,-14 150,0 V 80 H 0 Z; m 0,40 c 50,-12 94,-13 150,0 58,14 93,12 150,0 V 80 H 0 Z " keyTimes="0.00;0.50;1.00" /> </path>
I don't want to spend to much time on this -- the point here is that the simple wave effect I achieved by animating the Bezier nodes and handles under the (mistaken) assumption that I could simulate a traveling wave with this approach. But if you look at the animation -- while the "water" looks like it's rising and falling you don't get the aparrent motion I was after with the wave energy.
Fine, I thought. Maybe if I try adding a few more key frames I can get my traveling wave. If you do look closely at the SMIL code you'll see that I have three keyframes, an initial path, a path in "antiphase" for the sort of sinusoid I created and a final key back to the initial morphology. I figured by adding some 'tweens I could get the right aparent motion still using Bezier handles. And, indeed, the result got me a little closer to where I wanted to be. Here's something that looks a little closer to the end game I was after, but still it has it's problems.
Here we do see the apparent motion seen with a traveling wave. The above animation simulated the sort of wave propagation you might observe for example with water in a tank. The waves appear to be moving, and the flow of "energy" has speed and direction. But, alas we don't see that perfect continuity you might get with a pure traveling wave effect. Instead, the wave morphology degenerates and gets all "wobbly". Like traveling jello.
Normally I wouldn't spend time documenting a fail like this. But in this case, I feel it's worth it to understand why the current approach can't work before getting into the right approach to achieve this class of effect.
What we have here does travel, but it suffers from the classic "morph wobble": when keyframes are too sparse or inconsistent in control point behavior, SMIL interpolates Bézier curves in a naïve way, leading to:
- Amplitude warping
- Non-uniform phase velocity
which is just a fancy way of saying lumpy/wobbly distortion as we see above. If you really want a clean, smooth traveling wave effect, you need more frames. And after a certain limit generating keys by hand becomes excruciating.
Procedural Wave Animation with SVG
So having seen what doesn't work let's talk about what does. Elswhere I've written about the virtues of using SMIL to animate SVG [[CROSS REFERENCE CHESIRE CAT]]. You can create a lot of great art using Bezier paths and SMIL key frames. But as we just saw there are some cases where manually creating keys is just not an option -- and that's where procedural techniques come into play.
Procedural art is a form of artistic expression where artwork is created with the use of algorithms and mathematical formulas to dynamically generate content.
At the heart of procedural art lies the development of rules, procedures, mathematical functions etc. that determine the nature of the artwork that is produced. In addition to traditional methods analogous to drawing and sculpting, procedural artists manifest their intent through application of procedures and processes that enable them to generate and composite results. Here we'll look at the application of sine-based parametric procedural morphing to create SVG artworks inspired by the traveling wave. It sounds like a mouthful but the core idea is beautiful in its simplicity:
-
Procedurally generate a waveform using basic trigonometry,
-
Generate animation keys by convering points to cubic Bézier segments using a simple heuristic.
This gives us the keys necessary to create the intended animation insuring that we have enough segments and uniform Bézier paths obtain the desired effect. The result is visually clean, easily tunable, and free of the morph wobble you get with naive Bezier interpolation.
Figure 3 shows a wave animation I created using this approach. Just for fun I included a diver sprite "surfing the swell" to show how the wave now has an amplitude and velocity. You can see the diver moving along with the peak of the wave.
The Math behind the Morph
The math behind this particular procedural morph is relatively straightforward. To begin, we generate a curve by sampling points using the equation for a sine wave (see also [[ cross reference nn's sine effects blog part I ]]):
$$ y = Amplitude \cdot sin(2 \pi x / \lambda + \Phi) $$
In this equation, $\lambda$ (pronounced | lamb da |) is the wavelength and $\Phi$ (pronounced | fi |) is the phase shift (the amount by which the samples are shifted along x). To generate the keys for the animation I've used an algorithm to: (1) shift the wave across a predetermined number of intervals, and (2) sample the wave across a predefined period to create the segments. As an added bonus for this blog, below I've inlined an interface to the tool I developed to implement the algorithm. You can play with the tool to explore the effect of varying the paremeters from the equation.
Wave Effects Continued: The Serpentine Skeleton
Up to this point I've shown how to render "traveling wave energy" with pure trigonometry and SVG. But waves aren't just for water any more! In fact, the techniques we've developed up to this point can be used to create a large class of animated artworks based on the apparent motion of the traveling wave. By way of example, let's work through the serpentine skeleton I created for this piece (seen inlined below).
The Conceptual Approach
The core idea behind the serpentine skeleton was to turn the traveling wave curve into a 2D shape to animate the dragon. To do that we need to get a bit more creative with the trigonometry. But the core idea is eloquent in its simplicity: think of the rig as a skeleton and the animated shape(s) as an outer skin.
- Create a center line or spine (think of it like the spine of a fish).
- Sample the line along it's length and get tangent lines (which gives you the slope of the curve at those points).
- Get perpendicular vectors normalized to unit length -- the normals . This makes it easy to scale them outward, allowing you to derive edge points which can be used to create an SVG path for the final shape.
Here's some art to help with a visualization.

To illustrate the approach, I created an SVG artwork showing a sinusoid over which I've overlaid a set of samples (the blue circles) and drawn the tangents (as green vectors) and the normals (in red). So how do we get the tangents and normals? For that we need to apply a little math.
Doing the Math
Once again, we apply the wave equation to generate the curve (Inlined below for convenience):
$$ y = Amplitude \cdot sin(2 \pi x / \lambda + \Phi) $$
Once we have the curve, we can compute the tangent vectors using a wee bit of calculus. If you've not seen the calc before don't worry! That's why I'm explaining it here. And once I've made the framework and tooling for the SVG Creators Collaborative™ widely avalable the math is handled "under the hood". Still, it's good to understand at some level what's happening in order to get creative with the framework.
So, to compute the tangents we apply a bit of calculus:
Let ...
$$ B = \frac{ 2 \pi }{\lambda} $$
This substition gives us:
$$ y = Amplitude \cdot sin( B x + \Phi) $$
Then we can compute the derivative $\frac{dy}{dx}$ at each sample using [[APPENDIX: GETTING THE DERIVATIVE]] :
$$ \frac{d}{dx} y = Amplitude \cdot B \cdot cos( B x + \Phi) $$
Definition
In 2D and 3D computer graphics, normals are normalized vectors (lines of length = 1) sampled over an edge or surface and used in a range of applications (e.g., shading artwork).
Fixing the Endpoint
The Serpentine Skeleton works great for animation where movement follows a sinusoidal path. But another similar pattern requires one end of the wave path to be fixed. Think seaweed flowing in ocean currents, hair blowing in the wind or flags billowing on a pole.
To get a fixed endpoint effect like that we have to adjust our formula. To get a proper "free end" effect (like a flag flapping or seaweed swaying), the wave's amplitude needs to be zero at the fixed end and grow progressively towards the free end. One way to do this is to multiply the wave function by a factor that scales up with $x$. A nice way to do this and get another tunable parameter for our effect is to use a positive exponent -- let's call it the amplitude growth power. That would give us the following formula:
$$ y = \text{Amplitude} \times \left( \frac{x}{\lambda} \right)^P \cdot \sin( kx - \Phi ) $$
The $\left(\frac{x}{\lambda}\right)^P$ term ensures the amplitude starts at 0 when $x$ is 0 (giving us our fixed endpoint) and grows as $x$ increases giving us our flapping or swaying end. But the change impacts our calculations for the tangents. The derivative gets a bit more complex. We have to use the product rule:
$$ (f \cdot g)'(x) = f'(x)g(x) + f(x)g'(x) $$
where $$ f(x) = \left( \frac{x}{\lambda} \right)^P $$ and $$ g(x) = \sin( kx - \Phi ) $$
Then:
$$ \frac{d}{dx} y = \text{Amplitude} \left[ k \left( \frac{x}{\lambda} \right)^P \cos( kx - \Phi ) + P \frac{x^{P-1}}{\lambda^P} \sin( kx - \Phi ) \right] $$
Bonus!
More Tooling here ? Maybe with an AI interface ... ? Explaining the math ... ?
Gallery
INSERT GALLERY OF SVG ARTWORKS
CAT tail https://www.youtube.com/shorts/FjUqfhNEycM
mermaid references
nice to have sparkle texture ...
real seel 49sec into it https://www.youtube.com/watch?v=dN6FSKdKL3s
gorgeous animation 13 seconds https://www.tiktok.com/@anukitsu/video/7310313085548498183?lang=en
nice anim https://www.instagram.com/reel/DJmbiJYxtfN/?hl=en
funny animation https://www.instagram.com/reel/DKS3gmjhd_0/
Endnotes
-
For many, math is a "four-letter word". Believe me I understand that initial aversion. However, I truly hope you'll look past any fear of terms like trigonometry. It's my absolute favorite math, and my experience has shown me time and time again how powerful even a basic grasp of trig can be in crafting remarkable and beautiful works of art .
-
ENDNOTE THIS: Part of the inspiration for this article was from coming across this SnowWeb blog -- in particular their wave effect. It's what got me to thinking about doing a traveling wave in SVG.
Appendix A: Deriving the Tangent Using the Chain Rule
Since the starting point for abstracting the traveling wave effects is a periodic function (a sine wave) it's easy to get tangent lines at any point by applying some calculus. Since we start with the sine function:
$$ y = f(x) = A \cdot \sin( B x + \Phi ) $$
Where:
- $A$ is the amplitude,
- $B = \frac{2\pi}{\lambda}$ is constant (derived from the wavelength), and
- $\Phi$, also a constant, is the phase shift (horizontal offset).
We can compute the tangent at any point, $x$, along the curve by taking the derivative of the function, $\frac{dy}{dx}$, at that point.
We can take the derivative by applying the chain rule: If a function is composed of two functions ( like $y = f( u(x))$ ), then its derivative is:
$$ \frac{dy}{dx} = \frac{dy}{du} \cdot \frac{du}{dx} $$
Let the inner function be:
$$ u(x) = B \cdot x + \Phi $$
Then:
$$ y(x) = A \cdot \sin( u(x) ) $$
The derivative of $\sin( x )$ is $\cos( x )$ so:
$$ \frac{d}{dx} y = A \cdot \cos( u(x) ) \cdot \frac{d}{dx} u $$
The derivative of $u$ is $\frac{d}{dx} u = B$. So substitute:
$$ \frac{d}{dx} y= A \cdot \cos( B x + \Phi ) \cdot B $$
Rearrange for clarity by commutative property:
$$ \frac{dy}{dx} = A \cdot B \cdot \cos(Bx + \Phi) $$
This derivative gives us the operation we need to compute the slope tangent (the line giving us the slope of the curve) at each sampled point $x$ along our sine wave. Then it's all downhill to compute the normals using algebra creating the SVG shapes for our artwork.