Spectrum Analyzer

Hi everybody! I have recorded another video tutorial about shaders in Godot. This time, we will demonstrate how to create a spectrum analyzer that responds to music played by the Godot audio system. This is a nice effect known from various music applications and can be useful for enhancing your game. Let’s do it.

  1. Create a Node2D scene.
  2. Add a child node ColorRect and assign a new ShaderMaterial to it. Add a new shader to the material and fill the provided shader code.
  3. Add a child node AudioStreamPlayer and assign an audio file to its Stream property in the Inspector. Set Autoplay to On.
  4. Open Audio tab at the bottom of the Godot editor, and add SpectrumAnalyzer effect to Master Bus.
  5. Assign a new script to the root node and add this code:
extends Node2D


const VU_COUNT = 30
const FREQ_MAX = 11050.0
const MIN_DB = 60
const ANIMATION_SPEED = 0.1
const HEIGHT_SCALE = 8.0

@onready var color_rect = $ColorRect

var spectrum
var min_values = []
var max_values = []

func _ready():
	spectrum = AudioServer.get_bus_effect_instance(0, 0)
	min_values.resize(VU_COUNT)
	min_values.fill(0.0)
	max_values.resize(VU_COUNT)
	max_values.fill(0.0)

func _process(delta):
	var prev_hz = 0
	var data = []
	for i in range(1, VU_COUNT + 1):
		var hz = i * FREQ_MAX / VU_COUNT
		var f = spectrum.get_magnitude_for_frequency_range(prev_hz, hz)
		var energy = clamp((MIN_DB + linear_to_db(f.length())) / MIN_DB, 0.0, 1.0)
		data.append(energy * HEIGHT_SCALE)
		prev_hz = hz
	for i in range(VU_COUNT):
		if data[i] > max_values[i]:
			max_values[i] = data[i]
		else:
			max_values[i] = lerp(max_values[i], data[i], ANIMATION_SPEED)
		if data[i] <= 0.0:
			min_values[i] = lerp(min_values[i], 0.0, ANIMATION_SPEED)
	var fft = []
	for i in range(VU_COUNT):
		fft.append(lerp(min_values[i], max_values[i], ANIMATION_SPEED))
	color_rect.get_material().set_shader_parameter("freq_data", fft)
Shader code
shader_type canvas_item;

const int VU_COUNT = 30;
const float segment_count = 40.0;

uniform float[VU_COUNT] freq_data;

void fragment() {
	vec2 uv = UV;
	uv.y = 1.0 - uv.y;
	float vu_count = float(VU_COUNT);
	vec2 pos = vec2(floor(uv.x * vu_count) / vu_count, floor(uv.y * segment_count) / segment_count);
	float fft = freq_data[int(uv.x * vu_count)];
	vec3 color = mix(vec3(0.0, 2.0, 1.0), vec3(2.0, 0.0, 0.0), sqrt(uv.y));
	float mask = pos.y < fft ? 1.0 : 0.1;
	vec2 dist = fract((uv - pos) * vec2(vu_count, segment_count)) - 0.5;
	float led = smoothstep(0.5, 0.35, abs(dist.x)) * smoothstep(0.5, 0.35, abs(dist.y));
	COLOR = vec4(led * color * mask, 1.0);
}
Tags
2d, analyzer, audio, spectrum
The shader code and all code snippets in this post are under CC0 license and can be used freely without the author's permission. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from FencerDevLog

Edge Detection (Sobel Filter and Gaussian Blur)

Lightning

Animated Mirrored Ornament

Related shaders

Spectrum Displaying Shader

Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments