Aquarial Fish Wiggle Breakdown

Wiggling Fish


One of my favorite things about game jams is the creative outlet they provide. The projects we work on at Aesthetician Labs are all fulfilling in their own way, but are also tuned to meet the demands of the market. This is important if you’re trying to run a business, but it can be very satisfying to break away from those demands and make whatever you feel would be coolest. Often game jams give you the opportunity to develop your skills, learning new things you might not have time for in a tightly scheduled commercial project.

These are just some reasons why I’ve always liked to participate in the Ludum Dare game jam. For the unfamiliar, Ludum Dare is a bi-yearly game jam that requires you start, finish, and release a project, all within 72 hours. Even when we’re busy at AeLa, I try to make time for it, because I feel that I can make some of my most creative artistic work under the constraints of Ludum Dare.


As the jam began, I had a few ideas about what I wanted to make. The theme was “Deeper and Deeper”, which has a few obvious implications. I figured I could make a game about going deep into the earth(mining tycoon game), deep into a dungeon (roguelike dungeon crawler), or deep into the ocean(exploration game). My previous jam game, Aviarium, has the player controlling a bird going through its daily life. That game was quite successful, so I thought making another game with an animal theme could be good. I also recognized that my most successful jam game, Well Hidden Genie, was successful in part because of it’s fun, quirky dialogue. For a different project, I had been making 3D models of fish for a fishing minigame, which inspired me to make a game where you play a fish. This worked perfectly with the theme, as I could build a story where the player swims into deeper and deeper waters. From these inspirations, Aquarial was born, although I wouldn’t come up with that name until the last hours before submission.

Although I believed in the idea right from the beginning, I knew it would be one of my most ambitious jam games. I had so many ideas that I wanted to squeeze in the game. Some of the big largest features were cutscenes, dialogue, and a mission system. Because the idea was quite ambitious, I knew that building tools to increase how efficiently I could generate content for the game would be critically important to finish the game within 72 hours

One of the techniques I used to make Aquarial possible was the use of a custom shader. Shaders are used to control the graphics card of a computer, and they can be used to achieve a multitude of different effects. If you’re interested in learning more about shaders, I gave a presentation on them for ROC Game Dev in 2020. For this game, I used a geometry shader, which lets me change the shape of objects using math. This is advantageous, as once I had created the shader, I didn’t have to worry about animating any of the fish in the game! I simply had to move them through the world


All the heavy lifting in my shader is done in one line, shown below.

v.vertex.x += sin((_FishDistance + v.vertex.z) * _WigglePeriod) * _WiggleAmp;

Let’s break this down into its component parts. First, the variables: _FishDistance, _WigglePeriod, and _WiggleAmp.

_FishDistance is the distance the fish has traveled. This number is calculated in a C# script by taking the fish’s position each frame and comparing it to the distance from the previous frame. The distance between them is added to a variable and passed into the shader in each frame.

_WigglePeriod and _WiggleAmp are variables that control the amount of wiggle. _WiggleAmp controls how far the fish wiggles from side to side, and _WigglePeriod controls the length of each wiggle.

The next important thing to understand is what v.vertex.x represents. This line of code takes place in the vertex shader, which is used to calculate the position of each vertex in local space. Because local space is oriented around the fish, moving a vertex on the x-axis will always move it “side-to-side” relative to the fish. v.vertex.z represents a similar bit of information. It is the position of the current vertex on the z-axis of the fish (essentially, where does this vertex fall between the fish’s nose and its tail).

Now that we know what all these pieces of information mean, we can break the equation down. Let’s strip away everything except for the bare essentials.

Fish moving side to side

v.vertex.x += sin(_FishDistance);

The line above simply adds a sine value to each vertex’s position on the x-axis, based on how far the fish has travelled. This is close to the effect, but the entire fish wiggles back and forth all at once, instead of the fish wiggling. To solve this, we simply take into account the z-axis.

Fish moving side to side

v.vertex.x += sin(_FishDistance + v.vertex.z);

Now, the fish wiggles along its body, but we don’t have any control over the specifics. In order to control the period of the fish’s wiggle, we add a variable to multiply the distance traveled:

Fish moving side to side

v.vertex.x += sin((_FishDistance + v.vertex.z) * _WigglePeriod);

Then, to control the amplitude of the fish’s wiggle, we multiply the result of the sine wave operation:

Fish moving side to side

v.vertex.x += sin((_FishDistance + v.vertex.z) * _WigglePeriod) * _WiggleAmp;

And the result is a fully wiggling fish! If we set all of those variables to sensible values, we get this result:

Fish moving side to side

As a finishing touch, I multiply the amplitude by the speed of the fish. This makes it so that if the fish isn’t moving, the amplitude is zero, so it rests in a straight position instead of being permanently bent.

If you’d like to dig deeper into the code, I’ve made it available to view as a GitHub Gist.

If you have any questions you’d like to ask me about this, you can reach me via the AeLa Discord!

If you’d like to play Aquarial, check it out on!