When you making a game about hacking, you obviously need some sort of unrealistic visualization of the process. That's just a given. For our game, Egress, we wrote a shader program using Godot's native shading language that would display an cyclical-bar-visualizer-thingamajig:

The first step in the process was figuring out how to display the circle as individual bars that could be animated and scaled independently. This was done by getting the angle of a given UV coordinate with respect to the centre of the circle:
vec2 xy = UV - 0.5;
float angle = (atan(xy.y / xy.x));
Then, this angle was sorted into a section of the circle with a span equal to PI / number_of_bars
float section = angle - mod(angle, 3.14159 / float(bars));
Now that we have the section it falls into, we can then use this to generate a random value unique to that section by using a random function (thanks Stack Overflow!):
```
uniform float seed; // Can be used to seed the random number generation
// https://stackoverflow.com/questions/4200224/random-noise-functions-for-glsl
float rand(float value) {
return fract(sin(dot(vec2(value, seed), vec2(12.9898,78.233))) * 43758.5453);
}
```
Using this function, we can generate a random height for each bar and have that height also fluctuate using the same random value:
float radius = min_length + rand(section) * (max_length - min_length);
radius += pulsate_strength * sin(rand(section) * 6.282 + TIME * pulsate_speed);
The last piece of the puzzle is using this radius for rendering the object. This is done by determining if the UV coordinate lies within a circle of that radius, using the implicit function of a circle:
if (pow(xy.x, 2) + pow(xy.y, 2) < pow(radius, 2) &&
pow(xy.x, 2) + pow(xy.y, 2) > pow(inner_radius, 2)) {
COLOR = vec4(1.0);
} else {
COLOR = vec4(0.0);
}
As the output colour is white, we can then modulate the resulting texture to be whatever colour we want!
The Complete Code
```
shadertype canvasitem;
uniform float innerradius : hintrange(0, 0.5);
uniform float minlength : hintrange(0, 0.5) = 0.2;
uniform float maxlength : hintrange(0, 0.5) = 0.5;
uniform int bars : hintrange(2, 256);
uniform float seed;
uniform float rotationspeed;
uniform float pulsatespeed;
uniform float pulsatestrength;
// https://stackoverflow.com/questions/4200224/random-noise-functions-for-glsl
float rand(float value) {
return fract(sin(dot(vec2(value, seed), vec2(12.9898,78.233))) * 43758.5453);
}
vec2 rotate(vec2 uv, float rotation) {
vec2 rotated;
rotated.x = uv.x * cos(rotation) - uv.y * sin(rotation);
rotated.y = uv.x * sin(rotation) + uv.y * cos(rotation);
return rotated;
}
void fragment() {
vec2 xy = rotate(UV - 0.5, TIME * rotation_speed);
float angle = (atan(xy.y / xy.x));
float section = angle - mod(angle, 3.14159 / float(bars));
float radius = min_length + rand(section) * (max_length - min_length);
radius += pulsate_strength * sin(rand(section) * 6.282 + TIME * pulsate_speed);
if (pow(xy.x, 2) + pow(xy.y, 2) < pow(radius, 2) &&
pow(xy.x, 2) + pow(xy.y, 2) > pow(inner_radius, 2)) {
COLOR = vec4(1.0);
} else {
COLOR = vec4(0.0);
}
}
```
If you haven't checked out our game yet, we would love for you to give a shot and give some feedback!
It can be played in your browser here: https://lunatic-games.itch.io/egress