# 5. VFX GraphicsBuffers and Audio-Reactive Effects Part of [[VFX Graph]]. Previous: [[4. VFX GPU Events and Output Events]]. Next: [[6. VFX Graph Performance and Quest]]. > [!note] Verification status > `[VFXType(VFXTypeAttribute.Usage.GraphicsBuffer)]` appears in the 17.5 Sample Graphics Buffer operator page; `VisualEffect.SetGraphicsBuffer` / `HasGraphicsBuffer` exist in the 6000.5 Scripting Reference; the built-in **Audio Spectrum to AttributeMap** binder is listed on the 17.5 Property Binders page (2026-11-10). Exact operator/port names in the graph UI may differ slightly by version. **Code not compiled in Unity.** ## 5.1 Why GraphicsBuffers Exposed properties are fine for a handful of values. For *arrays* - 512 spectrum bins, 10,000 enemy positions, a path of 200 points - push one **structured GraphicsBuffer** per frame and sample it on the GPU. One contiguous copy of tightly packed structs is **Data Locality** at the CPU-GPU boundary ([[Game Programming Patterns (Review)]]). ## 5.2 Script: audio spectrum to VFX The graph: a ring of particles (capacity 512, spawned once). Each particle reads its bin from the buffer and scales its height/color by the bin value. ```csharp // Assets/Scripts/VFX/AudioSpectrumToVfx.cs using UnityEngine; using UnityEngine.VFX; [RequireComponent(typeof(VisualEffect))] public class AudioSpectrumToVfx : MonoBehaviour { public AudioSource source; // music, or a Microphone/line-in clip (see below) [Tooltip("Power of two between 64 and 8192")] public int bins = 512; public FFTWindow window = FFTWindow.BlackmanHarris; [Tooltip("Smoothing: 0 = raw, closer to 1 = slower decay")] [Range(0f, 0.99f)] public float smoothing = 0.8f; public float gain = 40f; static readonly int kBuffer = Shader.PropertyToID("SpectrumBuffer"); static readonly int kCount = Shader.PropertyToID("SpectrumCount"); VisualEffect vfx; GraphicsBuffer buffer; float[] raw; float[] smoothed; void OnEnable() { vfx = GetComponent<VisualEffect>(); bins = Mathf.ClosestPowerOfTwo(Mathf.Clamp(bins, 64, 8192)); raw = new float[bins]; smoothed = new float[bins]; buffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, bins, sizeof(float)); if (vfx.HasGraphicsBuffer(kBuffer)) vfx.SetGraphicsBuffer(kBuffer, buffer); if (vfx.HasUInt(kCount)) vfx.SetUInt(kCount, (uint)bins); } void Update() { if (source == null) return; source.GetSpectrumData(raw, 0, window); for (int i = 0; i < bins; i++) { float v = raw[i] * gain * (1f + i * 0.02f); // tilt: highs are quieter in FFT smoothed[i] = Mathf.Max(v, smoothed[i] * smoothing); // fast attack, slow release } buffer.SetData(smoothed); } void OnDisable() { buffer?.Release(); buffer = null; } } ``` **Graph wiring** 1. Blackboard: `SpectrumBuffer` (**Graphics Buffer**), `SpectrumCount` (**uint**). Expose both. 2. Spawn: **Single Burst** 512 on play. Initialize: capacity 512; position on a circle using `particleId / SpectrumCount` as the angle. 3. Update: **Sample Graphics Buffer** (type float) with index = `particleId`; use the value to set `scale.y` and lerp `color`. 4. Output: Mesh output (thin cubes) or quads, additive. **Line-in from a synth.** Marshall's Moog Subharmonicon / Mother-32 can feed an audio interface; record it into an `AudioClip` with `Microphone.Start(device, loop: true, lengthSec: 1, sampleRate)`, assign to the AudioSource, set `loop = true`, play once `Microphone.GetPosition(device) > 0`. Mute the output via a mixer group, not `volume = 0` (spectrum is read post-volume). See [[Eurorack and Unity]] and the CPU version in [[Music Visualization]]. ## 5.3 Script: swarm positions to particles Thousands of agents simulated elsewhere (CPU jobs or ECS - see [[DOTS]]) rendered as particles. The struct carries what the GPU needs and nothing more. ```csharp // Assets/Scripts/VFX/SwarmToVfx.cs using System.Runtime.InteropServices; using Unity.Collections; using UnityEngine; using UnityEngine.VFX; [VFXType(VFXTypeAttribute.Usage.GraphicsBuffer)] [StructLayout(LayoutKind.Sequential)] public struct SwarmPoint { public Vector3 position; public float heat; // 0..1, e.g. how close to attacking -> color } [RequireComponent(typeof(VisualEffect))] public class SwarmToVfx : MonoBehaviour { public int maxPoints = 10000; static readonly int kBuffer = Shader.PropertyToID("SwarmBuffer"); static readonly int kCount = Shader.PropertyToID("SwarmCount"); VisualEffect vfx; GraphicsBuffer buffer; void OnEnable() { vfx = GetComponent<VisualEffect>(); buffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, maxPoints, Marshal.SizeOf<SwarmPoint>()); vfx.SetGraphicsBuffer(kBuffer, buffer); vfx.SetUInt(kCount, 0); } /// Call once per frame from your simulation with a NativeArray (no managed copies). public void Push(NativeArray<SwarmPoint> points, int count) { count = Mathf.Min(count, maxPoints); buffer.SetData(points, 0, 0, count); vfx.SetUInt(kCount, (uint)count); } void OnDisable() { buffer?.Release(); buffer = null; } } ``` **Graph wiring:** Initialize capacity = `maxPoints`; spawn all once. Update: **Sample Graphics Buffer** (type `SwarmPoint`) at `particleId`; set `position`; set `alive = particleId < SwarmCount` so unused slots are hidden; map `heat` to color. The particles become a GPU-side render of the swarm, with zero GameObjects. **Explanation** - `Marshal.SizeOf<SwarmPoint>()` = 16 bytes (float3 + float). Keep structs a multiple of 4 bytes; 16-byte alignment is friendliest for GPUs. - `SetData(NativeArray, srcStart, dstStart, count)` uploads only live entries. - Always `Release()` GraphicsBuffers; leaking them shows up as GPU memory growth and editor warnings. ## 5.4 Exercise Drive the spectrum ring from a Mother-32 patch. Map low bins to the ring's radius and high bins to sparkle spawn rate. Then hand the same `SpectrumBuffer` to the landmark beacon so the world pulses with the music - a diegetic music visualizer. ## Sources - Sample Graphics Buffer operator (VFXType usage): https://docs.unity3d.com/Packages/[email protected]/manual/Operator-SampleBuffer.html - `VisualEffect` (SetGraphicsBuffer, HasGraphicsBuffer, SetUInt): https://docs.unity3d.com/6000.5/Documentation/ScriptReference/VFX.VisualEffect.html - Property Binders (Audio Spectrum to AttributeMap): https://docs.unity3d.com/Packages/[email protected]/manual/PropertyBinders.html - Data Locality: https://gameprogrammingpatterns.com/data-locality.html