Reference Devices compiles

radio_device: Music and Audio That Reacts to Your Game

The `radio_device` lets you play curated music and sound effects that respond to live game events — start a tense heartbeat when a player hides too long, cut the music when a round ends, or reveal a hidden radio as a collectible reward. Unlike a static audio track, `radio_device` can target individual players via `Register`/`Unregister`, so each player hears exactly what the game wants them to hear.

Updated Examples verified on the live UEFN compiler
Watch the Knotradio_device in ~90 seconds.

Overview

The radio_device is a Creative device that plays audio — music, ambient sound, or sound effects — either globally or targeted at specific registered agents. You place it in your UEFN level, configure the audio clip and volume in its User Options panel, then drive it entirely from Verse at runtime.

When to reach for it:

  • Background music that starts/stops based on game phase (lobby → combat → victory)
  • Per-player audio cues (a heartbeat that only the hiding player hears)
  • A hidden radio prop that appears and starts playing when a player finds a secret area
  • Cutting all music on elimination or round end

The key distinction from a static audio actor: Register(Agent) makes the radio play from that agent's perspective, letting you deliver personalised audio without extra devices.

API Reference

radio_device

Used to play curated music from the device or one or more registered agents.

Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).

radio_device<public> := class<concrete><final>(creative_device_base):

Methods (call these to make the device act):

Method Signature Description
Play Play<public>():void Starts playing audio from this device.
Stop Stop<public>():void Stops playing audio from this device.
Register Register<public>(Agent:agent):void Adds the specified agent as a target for the Radio to play audio from
Unregister Unregister<public>(Agent:agent):void Removes the specified agent as a target for the Radio to play audio from if previously Registered
UnregisterAll UnregisterAll<public>():void Removes all previously registered agents as targets for the Radio to play audio from
Show Show<public>()<transacts>:void Shows this device in the world.
Hide Hide<public>()<transacts>:void Hides this device from the world.

Walkthrough

Scenario: A prop-hunt map. When a prop player stands still for 5 seconds, a heartbeat radio starts playing — registered only to that player — so hunters nearby can hear it. When the player moves (steps on a trigger), they are unregistered and the heartbeat stops for them. A second ambient radio plays global background music from the start and is hidden so players can't see it in the world.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Fortnite.com/Playspaces }
using { /Fortnite.com/Characters }

# Converts a plain string into a localizable message for UI/log use.
HeartbeatLog<localizes>(S : string) : message = "{S}"

prop_hunt_radio_manager := class(creative_device):

    # The heartbeat radio — configured in UEFN with a looping heartbeat audio clip.
    @editable
    HeartbeatRadio : radio_device = radio_device{}

    # Ambient background music radio — always playing, hidden from players.
    @editable
    AmbientRadio : radio_device = radio_device{}

    # A trigger the prop player steps on when they move (e.g. a movement sensor trigger).
    @editable
    MovementTrigger : trigger_device = trigger_device{}

    # Tracks which agents currently have the heartbeat registered.
    var RegisteredAgents : []agent = array{}

    OnBegin<override>()<suspends> : void =
        # Hide the ambient radio prop so players can't see the device in the world.
        AmbientRadio.Hide()

        # Start the ambient background music immediately — heard by everyone.
        AmbientRadio.Play()

        # Subscribe to the movement trigger so we can unregister movers.
        MovementTrigger.TriggeredEvent.Subscribe(OnPlayerMoved)

        # Simulate registering all players to the heartbeat after 5 seconds of game start.
        # In a real map you'd hook this to a "player stood still" event.
        Sleep(5.0)
        RegisterAllPlayersToHeartbeat()

    # Registers every current player to the heartbeat radio.
    RegisterAllPlayersToHeartbeat() : void =
        Playspace := GetPlayspace()
        Players := Playspace.GetPlayers()
        for (P : Players):
            HeartbeatRadio.Register(P)
            set RegisteredAgents = RegisteredAgents + array{P}
        # Start the heartbeat — now each registered player hears it.
        HeartbeatRadio.Play()

    # Called when a player steps on the movement trigger — unregister them from heartbeat.
    OnPlayerMoved(Agent : ?agent) : void =
        if (A := Agent?):
            HeartbeatRadio.Unregister(A)
            # Remove from our local tracking array.
            set RegisteredAgents = for (R : RegisteredAgents, R <> A) { R }

            # If nobody is registered any more, stop the heartbeat entirely.
            if (RegisteredAgents.Length = 0):
                HeartbeatRadio.Stop()

Line-by-line explanation:

Lines What's happening
@editable HeartbeatRadio Wires the Verse class to the placed radio_device in the level. Without @editable the device can't be referenced.
AmbientRadio.Hide() Hides the device mesh so players don't see a floating radio prop — audio still plays.
AmbientRadio.Play() Starts global background music. No agents registered = everyone hears it.
MovementTrigger.TriggeredEvent.Subscribe(OnPlayerMoved) Hooks the movement trigger to our handler.
Sleep(5.0) Waits 5 seconds before registering players — simulates "stood still" detection.
HeartbeatRadio.Register(P) Targets the heartbeat at each player individually.
HeartbeatRadio.Play() Starts the heartbeat; only registered agents hear it.
if (A := Agent?) Safely unwraps the ?agent the trigger event sends.
HeartbeatRadio.Unregister(A) Removes the moving player — they no longer hear the heartbeat.
HeartbeatRadio.Stop() Stops the device entirely once no one is registered.

Common patterns

Pattern 1 — Stop all audio and clear all registered agents at round end

Use Stop and UnregisterAll together to cleanly reset audio state when a round finishes.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }

round_end_audio_cleaner := class(creative_device):

    @editable
    BattleMusic : radio_device = radio_device{}

    @editable
    RoundEndTrigger : trigger_device = trigger_device{}

    OnBegin<override>()<suspends> : void =
        # Start battle music at round begin.
        BattleMusic.Play()
        # Wait for the round-end trigger (e.g. all enemies eliminated).
        RoundEndTrigger.TriggeredEvent.Subscribe(OnRoundEnded)

    OnRoundEnded(Agent : ?agent) : void =
        # Remove every registered agent so no one hears stale audio next round.
        BattleMusic.UnregisterAll()
        # Stop the music immediately.
        BattleMusic.Stop()

Pattern 2 — Reveal a hidden radio as a collectible secret

Use Show and Play together to make a radio appear and start playing when a player finds a secret area.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }

secret_radio_revealer := class(creative_device):

    # Radio starts hidden in UEFN (set Visible = false in device settings as default).
    @editable
    SecretRadio : radio_device = radio_device{}

    # A trigger placed inside the secret room.
    @editable
    SecretRoomTrigger : trigger_device = trigger_device{}

    OnBegin<override>()<suspends> : void =
        # Make sure the radio is hidden at game start.
        SecretRadio.Hide()
        SecretRoomTrigger.TriggeredEvent.Subscribe(OnSecretFound)

    OnSecretFound(Agent : ?agent) : void =
        # Reveal the radio prop in the world.
        SecretRadio.Show()
        # Start playing — the discoverer and anyone nearby hears it.
        SecretRadio.Play()
        # Optionally register only the discovering player for a personal audio reward.
        if (A := Agent?):
            SecretRadio.Register(A)

Pattern 3 — Per-player audio: register one player, unregister another on elimination

Register a VIP player to a special theme, then unregister them when they are eliminated.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Fortnite.com/Playspaces }
using { /Fortnite.com/Characters }

vip_theme_manager := class(creative_device):

    @editable
    VIPThemeRadio : radio_device = radio_device{}

    # Trigger fired by a game-manager device when the VIP is chosen.
    @editable
    VIPChosenTrigger : trigger_device = trigger_device{}

    # Trigger fired when the VIP is eliminated.
    @editable
    VIPEliminatedTrigger : trigger_device = trigger_device{}

    var CurrentVIP : ?agent = false

    OnBegin<override>()<suspends> : void =
        VIPThemeRadio.Hide()
        VIPChosenTrigger.TriggeredEvent.Subscribe(OnVIPChosen)
        VIPEliminatedTrigger.TriggeredEvent.Subscribe(OnVIPEliminated)

    OnVIPChosen(Agent : ?agent) : void =
        if (A := Agent?):
            set CurrentVIP = option{A}
            VIPThemeRadio.Register(A)
            VIPThemeRadio.Play()

    OnVIPEliminated(Agent : ?agent) : void =
        if (VIP := CurrentVIP?):
            VIPThemeRadio.Unregister(VIP)
            set CurrentVIP = false
            VIPThemeRadio.Stop()

Gotchas

1. @editable is mandatory — you cannot construct a radio_device in code. Every radio_device you want to control must be placed in the UEFN level and wired via an @editable field. Writing MyRadio := radio_device{} and calling methods on it will compile but will have no effect because there is no placed device behind it.

2. Register targets audio at an agent, but Play must still be called. Calling Register(Agent) alone does nothing audible. You must also call Play() to start the audio. Conversely, calling Play() before any agents are registered broadcasts to everyone — decide which behaviour you want before writing the sequence.

3. Show and Hide are <transacts> — they participate in Verse's speculative execution. If you call Show() or Hide() inside a failing if branch that gets rolled back, the visibility change is also rolled back. This is usually what you want, but be aware when mixing them with <decides> expressions.

4. ?agent unwrap is required in every event handler. Trigger events send ?agent (an optional agent). Always unwrap with if (A := Agent?): before passing to Register or Unregister — passing a ?agent directly where agent is expected is a compile error.

5. Audio clip is set in the editor, not in Verse. You cannot change which audio clip a radio_device plays from Verse. The clip, volume, and looping behaviour are all configured in the device's User Options panel in UEFN. Verse only controls playback state and registration.

6. UnregisterAll then Stop — order matters for clean resets. Call UnregisterAll() before Stop() when resetting between rounds. If you call Stop() first and then immediately Play() in the next round, any lingering registrations from the previous round will still be active.

Device Settings & Options

The radio_device User Options panel in the UEFN editor — every setting you can tune.

Radio Device settings and options panel in the UEFN editor — Audio, Volume, Visible in Game
Radio Device — User Options in the UEFN editor: Audio, Volume, Visible in Game
⚙️ Settings on this device (3)

Setting names read from the panel above — may be partial; the screenshots are the source of truth.

Audio
Volume
Visible in Game

Guides & scripts that use radio_device

Step-by-step tutorials that put this object to work.

Build your own lesson with radio_device

Generate a personalized, step-by-step lesson plan built around this object — grounded in this exact reference and our compile-verified knowledge base.

Build a lesson →