Quick answer: The most common cause is a missing or misconfigured Signal Receiver component. The Signal Emitter on the Timeline track emits a Signal Asset, but a Signal Receiver component on the bound GameObject must be listening for that exact Signal Asset and have a reaction (UnityEvent) configured.
Here is how to fix Unity timeline playable not triggering events. Your cutscene plays perfectly in the Timeline editor, but when you hit Play the events never fire. Doors do not open, sound effects do not trigger, cameras do not switch. The animation plays fine—it is only the event callbacks that silently fail. This is one of the most common Timeline issues and it almost always comes down to how Signal Emitters, Signal Receivers, and PlayableDirector are configured.
How Timeline Signals Work
Timeline uses a three-part system for events. A Signal Asset is a ScriptableObject that acts as an identifier—it is just a named token. A Signal Emitter is a marker placed on a Timeline track at a specific time; it references a Signal Asset. A Signal Receiver is a component on a GameObject that listens for a specific Signal Asset and invokes a UnityEvent when it is received.
For a signal to fire at runtime, all three parts must be correctly connected. The Signal Emitter must reference a valid Signal Asset, the Signal Receiver must be on the correct GameObject, and the reaction event on the receiver must have at least one listener configured.
Solution 1: Verify Signal Receiver Binding
The most common cause of signals not firing is that the Signal Receiver is on the wrong GameObject or is not bound to the correct track. The Signal Receiver must be on the same GameObject that the Signal Track (or the track containing the Signal Emitter) is bound to in the PlayableDirector.
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
public class SignalBindingValidator : MonoBehaviour
{
[SerializeField] private PlayableDirector director;
void Start()
{
if (director == null || director.playableAsset == null) return;
TimelineAsset timeline = director.playableAsset as TimelineAsset;
if (timeline == null) return;
foreach (TrackAsset track in timeline.GetOutputTracks())
{
// Check each marker on the track
foreach (IMarker marker in track.GetMarkers())
{
if (marker is SignalEmitter emitter)
{
// Get the bound object for this track
Object binding = director.GetGenericBinding(track);
if (binding == null)
{
Debug.LogWarning($"Signal '{emitter.asset?.name}' on track '{track.name}' " +
"has no bound object. Signal will not fire.");
continue;
}
// Check if the bound object has a SignalReceiver
GameObject boundObj = null;
if (binding is GameObject go) boundObj = go;
else if (binding is Component comp) boundObj = comp.gameObject;
if (boundObj != null && boundObj.GetComponent<SignalReceiver>() == null)
{
Debug.LogError($"Signal '{emitter.asset?.name}' targets '{boundObj.name}' " +
"but it has no SignalReceiver component.");
}
}
}
}
}
}
To fix this: select the GameObject that the Signal Track is bound to in the PlayableDirector bindings list. Add a Signal Receiver component to that exact GameObject. In the Signal Receiver, add a reaction for your Signal Asset and wire up the UnityEvent to the method you want called.
Solution 2: Fix PlayableDirector Wrap Mode
If your signal is at the very end of the Timeline, the Wrap Mode setting on the PlayableDirector determines whether it fires. With None, playback stops when it reaches the end, and a signal on the last frame may not trigger because the director stops before evaluating it.
using UnityEngine;
using UnityEngine.Playables;
public class TimelineEventController : MonoBehaviour
{
[SerializeField] private PlayableDirector director;
void Awake()
{
// Use Hold mode to ensure the last frame is evaluated
director.extrapolationMode = DirectorWrapMode.Hold;
// Subscribe to the stopped event to handle end-of-timeline logic
director.stopped += OnTimelineStopped;
}
private void OnTimelineStopped(PlayableDirector d)
{
Debug.Log("Timeline finished. Running end-of-cutscene logic.");
// Put critical end-of-timeline events here as a safety net
}
void OnDestroy()
{
if (director != null)
director.stopped -= OnTimelineStopped;
}
}
A practical workaround is to move your end-of-timeline Signal Emitter slightly before the actual end—even 0.01 seconds earlier. This ensures the playhead crosses the signal before the director evaluates the stop condition.
Solution 3: Use Custom Notifications Instead of Signals
If Signal Emitters continue to be unreliable, you can replace them with custom notifications using the INotificationReceiver interface. This gives you direct control over how and when events fire during timeline playback.
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
// Custom marker that carries event data
[System.Serializable]
public class CustomEventMarker : Marker, INotification
{
public string eventName;
public int eventData;
public PropertyName id => new PropertyName("CustomEventMarker");
}
// Receiver that handles the notification
public class CustomEventReceiver : MonoBehaviour, INotificationReceiver
{
public void OnNotify(Playable origin, INotification notification, object context)
{
if (notification is CustomEventMarker marker)
{
Debug.Log($"Timeline event received: {marker.eventName} with data: {marker.eventData}");
switch (marker.eventName)
{
case "OpenDoor":
HandleDoorOpen(marker.eventData);
break;
case "PlaySFX":
HandleSoundEffect(marker.eventData);
break;
case "SwitchCamera":
HandleCameraSwitch(marker.eventData);
break;
}
}
}
private void HandleDoorOpen(int doorId) { /* door logic */ }
private void HandleSoundEffect(int sfxId) { /* audio logic */ }
private void HandleCameraSwitch(int camId) { /* camera logic */ }
}
Custom notifications fire through the PlayableDirector’s output system and do not require a separate Signal Receiver component. The INotificationReceiver component just needs to be on the GameObject that owns the PlayableDirector.
Solution 4: Handle Runtime vs Editor Differences
Timeline behaves differently in the editor versus at runtime. In the editor, scrubbing the playhead can trigger signals multiple times or skip them entirely. At runtime, signals only fire when the playhead moves forward past the marker time during normal evaluation.
using UnityEngine;
using UnityEngine.Playables;
public class TimelineDebugger : MonoBehaviour
{
[SerializeField] private PlayableDirector director;
void OnEnable()
{
director.played += OnPlayed;
director.paused += OnPaused;
director.stopped += OnStopped;
}
void OnDisable()
{
director.played -= OnPlayed;
director.paused -= OnPaused;
director.stopped -= OnStopped;
}
void Update()
{
if (director.state == PlayState.Playing)
{
Debug.Log($"Timeline time: {director.time:F3} / {director.duration:F3}");
}
}
private void OnPlayed(PlayableDirector d) => Debug.Log("Timeline started");
private void OnPaused(PlayableDirector d) => Debug.Log($"Timeline paused at {d.time:F3}");
private void OnStopped(PlayableDirector d) => Debug.Log("Timeline stopped");
}
Use this debugger to verify that the Timeline is actually playing through the time where your signal is placed. If the Timeline stops or pauses before reaching the signal time, that is your problem—not the signal system itself.
“Timeline signals are a three-legged stool: Signal Asset, Signal Emitter, Signal Receiver. If any one leg is missing or misconfigured, the whole thing falls over. Check all three before assuming the system is broken.”
Related Issues
If your Timeline animations play but look jittery or out of sync, see Fix: Unity Timeline Animation Jitter. For issues where Timeline clips overlap incorrectly, check Fix: Unity Timeline Clip Blending Not Working. To track cutscene bugs reported by playtesters, read Bug Reporting Tools for Unity Developers.
Verify all three parts of the signal chain: Asset, Emitter, Receiver. Check that the Signal Receiver is on the correct bound GameObject. Move end-of-timeline signals slightly before the last frame. Use the PlayableDirector stopped event as a fallback for critical end-of-cutscene logic.