A scriptable audio effect is a custom audio processor that transforms audio as it flows through the audio pipeline. An effect reads audio from an input buffer, processes it, and writes the result to an output buffer. Use effects to build filters, dynamics processors, distortion, or any other transformation of an audio signal.
The most common way to use an effect is to apply it to an AudioSource, where it processes the signal the audio source produces. You can also create effect instances directly in code, for example to nest an effect inside another processor.
To apply an effect to an audio source, or to define a reusable effect asset, implement the IAudioEffect interface, which acts as a factory for creating instances of your effect. Creating an instance directly in code doesn’t require IAudioEffect: you can call ControlContext.AllocateEffect yourself.
To apply an effect to an AudioSource:
MonoBehaviour that implements IAudioEffect.AudioSource.Unity discovers the component automatically and inserts the effect into the audio source’s signal chain. Unlike generators, you don’t assign the component to a field in the Inspector. The component’s presence on the GameObject applies the effect. This workflow is identical to built-in audio filter components, such as AudioLowPassFilter. For more information, refer to Use audio filters.
using UnityEngine;
using UnityEngine.Audio;
public class MyEffect : MonoBehaviour, IAudioEffect
{
public EffectInstance CreateInstance(
ControlContext context,
AudioFormat? nestedFormat,
EffectInstance.CreationParameters creationParameters)
{
return context.AllocateEffect(new MyRealtime(...), new MyControl(...), nestedFormat, creationParameters);
}
}
Unity calls CreateInstance when it discovers the component. This happens when:
Inside CreateInstance, initialize the effect from serialized fields or default values.
The AudioSource component owns the returned instance. The AudioSource destroys this instance when you remove the effect component, or you disable or destroy the audio source.
Stopping the audio source doesn’t destroy the instance. Unity deactivates the effect, but keeps the instance alive and reuses it when the audio source plays again. This means that CreateInstance doesn’t run a second time and Unity doesn’t call the Dispose method of your IControl implementation. Because of this, a stateful effect keeps its state across a stop and a subsequent play. To prevent the effect’s state from carrying over, you need to explicitly reset it. For example, send a message (ControlContext.SendMessage) to the effect after you call AudioSource.Play.
A domain reload destroys and re-creates the instance. Therefore, effect state doesn’t survive a domain reload.
You can add multiple effect components to the same GameObject. Unity processes them in component order, together with any built-in filter components such as AudioLowPassFilter, so you can reorder effects by reordering the components in the Inspector.
The following rules control how an effect participates in the audio source’s signal chain:
AudioSource to deactivate all effects on the audio source.To communicate with an effect while it plays, pass the component to AudioSource.GetEffectInstance and use the returned instance with the built-in control context, for example to send messages. Always guard the handle with ControlContext.Exists, because the instance might not exist yet or might have been destroyed.
var instance = audioSource.GetEffectInstance(myEffectComponent);
if (ControlContext.builtIn.Exists(instance))
{
var message = new MyMessage(...);
ControlContext.builtIn.SendMessage(instance, ref message);
}
For a complete walkthrough that sends parameter changes to an effect, refer to Configure the gain in Example: Create an effect.
IAudioEffect interfaceAn IAudioEffect implementation provides the CreateInstance factory method, which creates an EffectInstance. Unity calls it when an audio source discovers the effect component. For a nested effect, your parent processor calls it. Inside CreateInstance, allocate the instance with ControlContext.AllocateEffect, pairing a real-time struct that implements EffectInstance.IRealtime with a control struct that implements EffectInstance.IControl.
You can implement IAudioEffect on a MonoBehaviour to apply the effect to an audio source, or on a ScriptableObject to define a reusable, asset-based effect that other processors instantiate as a nested effect.
To serialize a reference to an IAudioEffect, declare a field of type IAudioEffect.Serializable on your component. Interface references aren’t directly serializable in user scripts, and this helper struct stores the reference for you.
Unity calls the Configure method of the effect’s control part when it creates the effect instance, and again whenever the audio system changes configuration. A configuration change typically happens in response to a user action, such as when a user changes the audio output device in the OS system settings or connects a pair of headphones.
Configure receives the AudioConfiguration the effect runs in, such as the sample rate and speaker mode. Use it to set any real-time fields that depend on the configuration, for example filter coefficients. During reconfiguration, the real-time part is temporarily suspended from processing, so you can safely modify its fields.
Configure also returns an EffectInstance.Setup through an out parameter. This type is reserved for future configure-time output and currently carries no data, so assign default.
Like generators, effects can be nested inside other processors. A parent processor creates a child effect with ControlContext.AllocateEffect, optionally passing a suggested AudioFormat, and is then responsible for the child’s lifecycle:
EffectInstance.Configure from the parent’s Configure method, but only when ControlContext.IsSystemWideReconfiguring is true. Unity configures root processors only, so a child that caches configuration-dependent state, such as filter coefficients, keeps stale values after a sample rate or output device change unless the parent forwards the new configuration. Don’t call it while you construct the child, because ControlContext.AllocateEffect already configures it, and EffectInstance.Configure throws outside a system-wide reconfiguration.ControlContext.Update from the parent’s control part.EffectInstance.Process from the parent’s real-time part, within a mix cycle. The input and output buffers must have the same channel and frame counts.ControlContext.Destroy from the parent’s Dispose method.Consider the following:
Process real-time safe. Avoid allocations, locks, blocking I/O, logging, and throwing exceptions on the audio thread. Use stack-allocated or pooled buffers, and struct-based states for predictable performance.UnityEngine APIs from within Process. Use pipes to synchronize state between the control and real-time part, and avoid any multithreaded communication that might yield indeterministic results.Unity.Mathematics and organize data for optimal Burst auto-vectorization.