# 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