Skip to content

Instantly share code, notes, and snippets.

@jangxx
Created July 7, 2025 20:23
Show Gist options
  • Save jangxx/9b9e273c1640efa5ab3695da1907a6dd to your computer and use it in GitHub Desktop.
Save jangxx/9b9e273c1640efa5ab3695da1907a6dd to your computer and use it in GitHub Desktop.
Circular Waveform ISF shader to be used in Magic Music Visuals
/*{
"CREDIT": "by jangxx",
"DESCRIPTION": "Circular waveform visualizer. Needs an AudioToImage node as input.\n\nradius:\nDistance from center to the waveform.\n\nline width:\nWidth of the waveform line.\n\nwave size:\nHow much the waveform expands and contracts.\n\nsolid:\nIf true, the waveform is fully filled and mirrored around the zero line.\n\ncircumference:\nHow much of the circle is used for the waveform, 0 = 0%, 1 = 100%.\n\nrotation:\nrotation of the waveform around the center.",
"INPUTS": [
{
"LABEL": "input image",
"NAME": "input",
"TYPE": "image"
},
{
"DEFAULT": 1,
"LABEL": "radius",
"NAME": "circle_radius",
"TYPE": "float",
"MIN": 0
},
{
"DEFAULT": 0.01,
"LABEL": "line width",
"NAME": "line_width",
"TYPE": "float",
"MIN": 0
},
{
"DEFAULT": 0.1,
"LABEL": "wave size",
"NAME": "wave_size",
"TYPE": "float",
"MIN": 0
},
{
"DEFAULT": false,
"LABEL": "solid",
"NAME": "solid",
"TYPE": "bool"
},
{
"DEFAULT": 1,
"LABEL": "cicumference",
"NAME": "circumference",
"TYPE": "float",
"MIN": 0,
"MAX": 1
},
{
"DEFAULT": 0,
"LABEL": "rotation",
"NAME": "rotation",
"TYPE": "float",
}
],
"ISFVSN": "2"
}
*/
#define PI 3.14159265359
void main() {
float aspect = RENDERSIZE.x / RENDERSIZE.y;
vec2 uv_center = (isf_FragNormCoord - vec2(0.5, 0.5)) * 2.0 * vec2(aspect, 1);
float dist = length(uv_center);
float angle = mod((atan(uv_center.y, -uv_center.x) + PI) / (2.0 * PI) + rotation, 1.0) / circumference;
float waveform = (IMG_NORM_PIXEL(input, vec2(angle, 0.5)).r - 0.5) * 2.0;
if (0 <= angle && angle <= 1) {
if (solid) {
float wave_thickness = line_width + waveform*wave_size;
if (dist <= circle_radius + wave_thickness && dist >= circle_radius - wave_thickness) {
gl_FragColor = vec4(1,1,1,1);
} else {
gl_FragColor = vec4(0,0,0,1);
}
} else {
float wave_radius = circle_radius + waveform * wave_size;
if (dist <= wave_radius + line_width/2 && dist >= wave_radius - line_width/2) {
gl_FragColor = vec4(1,1,1,1);
} else {
gl_FragColor = vec4(0,0,0,1);
}
}
}
}
/*{
"CREDIT": "by jangxx",
"DESCRIPTION": "Circular waveform visualizer. Needs an AudioToImage node as input.\n\nradius:\nDistance from center to the waveform.\n\nline width:\nWidth of the waveform line.\n\nwave size:\nHow much the waveform expands and contracts.\n\nrotation:\nrotation of the waveform around the center.\n\nvertex count:\nHow many vertices are used to draw the waveform, more vertices = smoother curve.",
"INPUTS": [
{
"LABEL": "input image",
"NAME": "input",
"TYPE": "image"
},
{
"DEFAULT": 1,
"LABEL": "radius",
"NAME": "circle_radius",
"TYPE": "float",
"MIN": 0
},
{
"DEFAULT": 0.01,
"LABEL": "line width",
"NAME": "line_width",
"TYPE": "float",
"MIN": 0
},
{
"DEFAULT": 0.1,
"LABEL": "wave size",
"NAME": "wave_size",
"TYPE": "float",
"MIN": 0
},
{
"DEFAULT": 0,
"LABEL": "rotation",
"NAME": "rotation",
"TYPE": "float",
},
{
"DEFAULT": 100,
"LABEL": "vertex count",
"NAME": "vertex_count",
"TYPE": "float",
}
],
"ISFVSN": "2"
}
*/
#define PI 3.14159265359
#define TWO_PI 6.28318530718
#define HALF_PI 1.57079632679
float angle(vec2 v1, vec2 v2) {
return acos(clamp(dot(v1, v2) / (length(v1) * length(v2)), -1.0, 1.0));
}
// https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line
float point_line_dist(vec2 l1, vec2 l2, vec2 p) {
return abs((l2.x - l1.x) * (l1.y - p.y) - (l1.x - p.x) * (l2.y - l1.y)) / length(l2 - l1);
}
void draw_rounded_line_segment(inout vec4 color, vec2 uv, vec2 p1, vec2 p2, float width, vec3 line_color) {
vec2 dir = p2 - p1;
float line_dist = point_line_dist(p1, p2, uv);
// Check if point is near the line segment (excluding the rounded ends)
if (line_dist <= width && angle(dir, uv - p1) <= HALF_PI && angle(-dir, uv - p2) <= HALF_PI) {
color.rgb = line_color;
}
// Check if point is near the start cap
else if (length(p1 - uv) <= width) {
color.rgb = line_color;
}
// Check if point is near the end cap
else if (length(p2 - uv) <= width) {
color.rgb = line_color;
}
}
void draw_point(inout vec4 color, vec2 uv, vec2 point, float width, vec3 point_color) {
float dist = length(uv - point);
if (dist <= width) {
color.rgb = point_color;
color.a = 1.0; // clamp((1.0 - dist / width) * 20.0, 0.0, 1.0);
}
}
void main() {
float aspect = RENDERSIZE.x / RENDERSIZE.y;
vec2 uv_center = (isf_FragNormCoord - vec2(0.5, 0.5)) * 2.0 * vec2(aspect, 1);
float rot_radians = -rotation * TWO_PI;
float rot_sin = sin(rot_radians);
float rot_cos = cos(rot_radians);
mat2 rotation_matrix = mat2(rot_cos, -rot_sin, rot_sin, rot_cos);
vec2 rotated_uv = rotation_matrix * uv_center;
float angle = atan(rotated_uv.x, rotated_uv.y) / TWO_PI;
// angle = mod(angle, 1.0);
float vc = round(vertex_count);
float idx_left1 = floor(angle * vc);
float pos_left1 = idx_left1 / vc;
float idx_left2 = mod(idx_left1 - 1, vc);
float pos_left2 = idx_left2 / vc;
float idx_right1 = ceil(angle * vc);
float pos_right1 = idx_right1 / vc;
float idx_right2 = mod(idx_right1 + 1, vc);
float pos_right2 = idx_right2 / vc;
float val_left2 = (IMG_NORM_PIXEL(input, vec2(fract(pos_left2), 0.5)).r - 0.5) * 2.0;
float val_left1 = (IMG_NORM_PIXEL(input, vec2(fract(pos_left1), 0.5)).r - 0.5) * 2.0;
float val_right1 = (IMG_NORM_PIXEL(input, vec2(fract(pos_right1), 0.5)).r - 0.5) * 2.0;
float val_right2 = (IMG_NORM_PIXEL(input, vec2(fract(pos_right2), 0.5)).r - 0.5) * 2.0;
vec2 point_left2 = vec2(sin(pos_left2 * TWO_PI), cos(pos_left2 * TWO_PI)) * (circle_radius + val_left2 * wave_size);
vec2 point_left1 = vec2(sin(pos_left1 * TWO_PI), cos(pos_left1 * TWO_PI)) * (circle_radius + val_left1 * wave_size);
vec2 point_right1 = vec2(sin(pos_right1 * TWO_PI), cos(pos_right1 * TWO_PI)) * (circle_radius + val_right1 * wave_size);
vec2 point_right2 = vec2(sin(pos_right2 * TWO_PI), cos(pos_right2 * TWO_PI)) * (circle_radius + val_right2 * wave_size);
vec4 color = vec4(0,0,0,0);
draw_rounded_line_segment(color, rotated_uv, point_left2, point_left1, line_width/2, vec3(1, 1, 1));
draw_rounded_line_segment(color, rotated_uv, point_left1, point_right1, line_width/2, vec3(1, 1, 1));
draw_rounded_line_segment(color, rotated_uv, point_right1, point_right2, line_width/2, vec3(1, 1, 1));
gl_FragColor = color;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment