Reference Verse compiles

Lerp: Smooth Interpolation for a Sunny Cove

The sun's out over the cove and you want a lagoon gate to glide open, not snap. `Lerp` is the one tiny math function that turns 'jump from A to B' into 'glide smoothly from A to B'. Pair it with a timer and a trigger, and you've got buttery island animations with a few lines of code.

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

Overview

A lerp (linear interpolation) is the simplest way to blend between two numbers. Verse's built-in Lerp(From, To, Parameter) returns From*(1 - Parameter) + To*Parameter. When Parameter is 0.0 you get From; at 1.0 you get To; at 0.5 you get exactly the midpoint. Feed it a Parameter that crawls from 0 to 1 over time (using Sleep in a loop) and any value — a height, a light intensity, a countdown position — moves smoothly instead of teleporting.

That's the game problem it solves: raw device state changes are instant and jarring. Reach for Lerp whenever you want a gradual transition — a sunlit cove gate rising, a pirate-ship plank easing down, a dock beacon fading up. Lerp itself isn't a device; it's a math built-in you drive from a creative_device script, usually kicked off by a trigger_device and timed against a timer_device.

In this article the island moment is a sunny lagoon cove: a player steps on a pressure plate wired to a trigger, a countdown timer starts, and a stone gate smoothly lerps up out of the water to reveal a hidden cove.

API Reference

timer_device

Provides a way to keep track of the time something has taken, either for scoreboard purposes, or to trigger actions. It can be configured in several ways, either acting as a countdown to an event that is triggered at the end, or as a stopwatch for an action that needs to be completed before a set time runs out.

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

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

Events (subscribe a handler to react):

Event Signature Description
SuccessEvent SuccessEvent<public>:listenable(?agent) Signaled when the timer completes or ends with success. Sends the agent that activated the timer, if any.
FailureEvent FailureEvent<public>:listenable(?agent) Signaled when the timer completes or ends with failure. Sends the agent that activated the timer, if any.
StartUrgencyModeEvent StartUrgencyModeEvent<public>:listenable(?agent) Signaled when the timer enters Urgency Mode. Sends the agent that activated the timer, if any.

Methods (call these to make the device act):

Method Signature Description
Enable Enable<public>(Agent:agent):void Enables this device for Agent.
Enable Enable<public>():void Enables this device.
Disable Disable<public>(Agent:agent):void Disables this device for Agent. While disabled this device will not receive signals.
Disable Disable<public>():void Disables this device. While disabled this device will not receive signals.
ResetForAll ResetForAll<public>(Agent:agent):void Resets the timer back to its base time and stops it for all agents.
ResetForAll ResetForAll<public>():void Resets the timer back to its base time and stops it for all agents.
Start Start<public>(Agent:agent):void Starts the timer for Agent.
Start Start<public>():void Starts the timer.
Pause Pause<public>(Agent:agent):void Pauses the timer for Agent.
Pause Pause<public>():void Pauses the timer.
Resume Resume<public>(Agent:agent):void Resumes the timer for Agent.
Resume Resume<public>():void Resumes the timer.
Complete Complete<public>(Agent:agent):void Completes the timer for Agent.
Complete Complete<public>():void Completes the timer.
StartForAll StartForAll<public>(Agent:agent):void Starts the timer for all agents.
StartForAll StartForAll<public>():void Starts the timer for all agents.
PauseForAll PauseForAll<public>(Agent:agent):void Pauses the timer for all agents.
PauseForAll PauseForAll<public>():void Pauses the timer for all agents.
ResumeForAll ResumeForAll<public>(Agent:agent):void Resumes the timer for all agents.
ResumeForAll ResumeForAll<public>():void Resumes the timer for all agents.
CompleteForAll CompleteForAll<public>(Agent:agent):void Completes the timer for all agents.
CompleteForAll CompleteForAll<public>():void Completes the timer for all agents.
Save Save<public>(Agent:agent):void Saves this device's data for Agent.
Load Load<public>(Agent:agent):void Loads this device's saved data for Agent.
ClearPersistenceData ClearPersistenceData<public>(Agent:agent):void Clears this device's saved data for Agent.
ClearPersistenceDataForAll ClearPersistenceDataForAll<public>(Agent:agent):void Clears this device's saved data for all agents.
ClearPersistenceDataForAll ClearPersistenceDataForAll<public>():void Clears this device's saved data for all agents.
SetActiveDuration SetActiveDuration<public>(Time:float, Agent:agent):void Sets the remaining time (in seconds) on the timer, if active, on Agent.
SetActiveDuration SetActiveDuration<public>(Time:float):void Sets the remaining time (in seconds) on the timer, if active. Use this function if the timer is set to use the same time for all agent's.
GetActiveDuration GetActiveDuration<public>(Agent:agent)<transacts>:float Returns the remaining time (in seconds) on the timer for Agent.
GetActiveDuration GetActiveDuration<public>()<transacts>:float Returns the remaining time (in seconds) on the timer if it is set to be global.
SetLapTime SetLapTime<public>(Agent:agent):void Sets the lap time indicator for Agent.
SetLapTimeForAll SetLapTimeForAll<public>(Agent:agent):void Sets the lap time indicator for all agents.
SetLapTimeForAll SetLapTimeForAll<public>():void Sets the lap time indicator for all agents.
SetMaxDuration SetMaxDuration<public>(Time:float):void Sets the maximum duration of the timer (in seconds).
GetMaxDuration GetMaxDuration<public>()<transacts>:float Returns the maximum duration of the timer (in seconds).
IsStatePerAgent IsStatePerAgent<public>()<transacts><decides>:void Succeeds if this device is tracking timer state for each individual agent independently. Fails if state is being tracked globally for all agent's.

trigger_device

Used to relay events to other linked devices.

Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse). Inherited members are merged from trigger_base_device.

trigger_device<public> := class<concrete><final>(trigger_base_device):

Events (subscribe a handler to react):

Event Signature Description
TriggeredEvent TriggeredEvent<public>:listenable(?agent) Signaled when an agent triggers this device. Sends the agent that used this device. Returns false if no agent triggered the action (ex: it was triggered through code).

Methods (call these to make the device act):

Method Signature Description
Trigger Trigger<public>(Agent:agent):void Triggers this device with Agent being passed as the agent that triggered the action. Use an agent reference when this device is setup to require one (for instance, you want to trigger the device only with a particular agent.
Trigger Trigger<public>():void Triggers this device, causing it to activate its TriggeredEvent event.
Enable Enable<public>():void Enables this device.
Disable Disable<public>():void Disables this device.
SetMaxTriggerCount SetMaxTriggerCount<public>(MaxCount:int):void Sets the maximum amount of times this device can trigger. * 0 can be used to indicate no limit on trigger count. * MaxCount is clamped between [0,20].
GetMaxTriggerCount GetMaxTriggerCount<public>()<transacts>:int Gets the maximum amount of times this device can trigger. * 0 indicates no limit on trigger count.
GetTriggerCountRemaining GetTriggerCountRemaining<public>()<transacts>:int Returns the number of times that this device can still be triggered before hitting GetMaxTriggerCount. Returns 0 if GetMaxTriggerCount is unlimited.
SetResetDelay SetResetDelay<public>(Time:float):void Sets the time (in seconds) after triggering, before the device can be triggered again (if MaxTrigger count allows).
GetResetDelay GetResetDelay<public>()<transacts>:float Gets the time (in seconds) before the device can be triggered again (if MaxTrigger count allows).
SetTransmitDelay SetTransmitDelay<public>(Time:float):void Sets the time (in seconds) which must pass after triggering, before this device informs other external devices that it has been triggered.
GetTransmitDelay GetTransmitDelay<public>()<transacts>:float Gets the time (in seconds) which must pass after triggering, before this device informs other external devices that it has been triggered.

vector3

3-dimensional vector with float components.

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

vector3<native><public> := struct<concrete><computes><persistable><uht_comparable>:

Walkthrough

Here's the full sunny-cove scene. When the player triggers the plate, we start a timer_device, then run a coroutine that lerps a float height from the closed position up to the open position over 3 seconds. We also read that height into a vector3 so you can see how lerp feeds real position math. When the timer's SuccessEvent fires, we make sure the gate is fully open.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/SpatialMath }

# The sunny lagoon cove gate controller.
cove_gate_device := class(creative_device):

    # The pressure plate at the water's edge that starts everything.
    @editable
    StartTrigger : trigger_device = trigger_device{}

    # A countdown timer shown to the player while the gate rises.
    @editable
    RiseTimer : timer_device = timer_device{}

    # Closed water-line height (cm) and fully-open height (cm).
    ClosedHeight : float = 0.0
    OpenHeight : float = 500.0

    OnBegin<override>()<suspends>:void =
        # React when a player steps on the plate.
        StartTrigger.TriggeredEvent.Subscribe(OnPlateStepped)
        # React when the countdown finishes cleanly.
        RiseTimer.SuccessEvent.Subscribe(OnTimerSuccess)

    # Handler for the pressure plate. Runs the smooth rise.
    OnPlateStepped(Agent : ?agent):void =
        # Give the countdown a 3 second window and start it for everyone.
        RiseTimer.SetMaxDuration(3.0)
        RiseTimer.StartForAll()
        # Kick off the async lerp animation.
        spawn { RaiseGate() }

    # Smoothly lerp the gate height from closed to open over 3 seconds.
    RaiseGate()<suspends>:void =
        Steps : int = 60          # 60 frames of animation
        Duration : float = 3.0     # total seconds
        var Step : int = 0
        loop:
            if (Step > Steps):
                break
            # Parameter walks 0.0 -> 1.0 across the steps.
            Parameter : float = Step * 1.0 / (Steps * 1.0)
            # The core call: blend closed height toward open height.
            Height : float = Lerp(ClosedHeight, OpenHeight, Parameter)
            # Feed the height into a position (X and Y stay put on the shore).
            GatePosition : vector3 = vector3{ X := 1000.0, Y := 2000.0, Z := Height }
            # (Here you'd move a prop to GatePosition; height is now animated.)
            set Step = Step + 1
            Sleep(Duration / (Steps * 1.0))

    # When the countdown succeeds, snap the gate to its final open height.
    OnTimerSuccess(Agent : ?agent):void =
        FinalHeight : float = Lerp(ClosedHeight, OpenHeight, 1.0)
        GatePosition : vector3 = vector3{ X := 1000.0, Y := 2000.0, Z := FinalHeight }
        # Gate guaranteed fully open even if timing drifted a frame.

Line by line:

  • @editable StartTrigger : trigger_device — you MUST declare the placed device as an editable field so Verse can find it; a bare trigger_device.Trigger() fails with 'Unknown identifier'.
  • OnBegin<override>()<suspends>:void = — the entry point. We subscribe our handlers here.
  • StartTrigger.TriggeredEvent.Subscribe(OnPlateStepped) — wires the plate's real TriggeredEvent to our method.
  • RiseTimer.SetMaxDuration(3.0) then RiseTimer.StartForAll() — real timer_device methods that give the player a visible 3-second countdown.
  • spawn { RaiseGate() } — launches the animation coroutine without blocking the handler.
  • Parameter : float = Step * 1.0 / (Steps * 1.0) — Verse never auto-converts int↔float, so we multiply by 1.0 to make floats before dividing.
  • Lerp(ClosedHeight, OpenHeight, Parameter) — the heart of it: at Step 0 the height is 0, at Step 60 it's 500, smoothly in between.
  • Sleep(Duration / (Steps * 1.0)) — waits ~0.05s per frame so the loop paces the animation across 3 seconds.
  • OnTimerSuccess uses Lerp(..., 1.0) to guarantee the exact open height when the countdown ends.

Common patterns

1. Fade a dock beacon's brightness with EaseIn timing. Instead of a straight Parameter, you can shape the curve. Here we still use Lerp for the value but let the parameter ramp accelerate for a gentle sunrise glow, driven off a trigger with a reset delay.

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

beacon_glow_device := class(creative_device):

    @editable
    GlowTrigger : trigger_device = trigger_device{}

    OnBegin<override>()<suspends>:void =
        # Let the beacon re-trigger every 5 seconds.
        GlowTrigger.SetResetDelay(5.0)
        GlowTrigger.TriggeredEvent.Subscribe(OnGlow)

    OnGlow(Agent : ?agent):void =
        spawn { FadeUp() }

    FadeUp()<suspends>:void =
        var T : float = 0.0
        loop:
            if (T > 1.0):
                break
            # EaseIn-style ramp: square the parameter for a slow start.
            Eased : float = T * T
            Brightness : float = Lerp(0.0, 100.0, Eased)
            # (Apply Brightness to your light prop here.)
            set T = T + 0.05
            Sleep(0.05)

2. Pause and resume a lerp with a timer's controls. A pirate-ship plank lowers, but if the timer is paused the animation waits. This shows Pause/Resume and Complete on timer_device.

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

plank_device := class(creative_device):

    @editable
    PlankTimer : timer_device = timer_device{}

    @editable
    LowerButton : trigger_device = trigger_device{}

    OnBegin<override>()<suspends>:void =
        LowerButton.TriggeredEvent.Subscribe(OnLower)
        PlankTimer.SuccessEvent.Subscribe(OnDone)

    OnLower(Agent : ?agent):void =
        PlankTimer.SetActiveDuration(2.0, GetDefaultAgent(Agent))
        PlankTimer.StartForAll()

    GetDefaultAgent(Agent : ?agent)<decides><transacts>:agent =
        Agent?

    OnDone(Agent : ?agent):void =
        # Plank hits the water: final lerp position at 100%.
        FinalDrop : float = Lerp(300.0, 0.0, 1.0)
        # (Move plank down to FinalDrop.)
        PlankTimer.CompleteForAll()

3. Lerp toward a target only when urgency mode kicks in. Speed up a lagoon light's color shift when the timer enters StartUrgencyModeEvent.

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

urgency_light_device := class(creative_device):

    @editable
    CountdownTimer : timer_device = timer_device{}

    OnBegin<override>()<suspends>:void =
        CountdownTimer.StartUrgencyModeEvent.Subscribe(OnUrgent)
        CountdownTimer.SetMaxDuration(10.0)
        CountdownTimer.StartForAll()

    OnUrgent(Agent : ?agent):void =
        spawn { FlashRed() }

    FlashRed()<suspends>:void =
        var T : float = 0.0
        loop:
            if (T > 1.0):
                break
            # Lerp light from calm (0) to alarm (255) fast.
            RedValue : float = Lerp(0.0, 255.0, T)
            # (Apply RedValue to the cove warning light.)
            set T = T + 0.1
            Sleep(0.02)

Gotchas

  • No int↔float auto-conversion. Lerp takes three floats. If your loop counter is an int, convert with * 1.0 before dividing or passing it in. Step / Steps on ints is integer division and will give you 0 for most of the loop.
  • Lerp extrapolates past 1.0. Lerp(0.0, 10.0, 2.0) returns 20.0, not 10.0. If you loop your Parameter past 1.0 the gate will overshoot. Clamp with a break when T > 1.0 (as shown) or guard your parameter.
  • Do the animation in a coroutine. Sleep requires a <suspends> context. Call your lerp loop via spawn { ... } from an event handler so the handler returns immediately and the animation runs alongside the game.
  • Unwrap ?agent before use. Event handlers receive (Agent : ?agent). If you need the agent (e.g. for SetActiveDuration(Time, Agent:agent)), unwrap it with if (A := Agent?): or a <decides> helper — you can't pass a ?agent where an agent is required.
  • InterpolationTypes.Ease/EaseIn/... are curve shapes, not Lerp replacements. They produce cubic_bezier_parameters for the CreativeAnimation system. For simple value blends in your own loop, shape the parameter yourself (e.g. T*T for ease-in) and keep calling Lerp.
  • Frame pacing vs. timer duration. Your Sleep-based loop and the timer_device countdown are independent. Use the timer's SuccessEvent to snap the final value (via Lerp(From, To, 1.0)) so tiny frame drift never leaves the gate half-open.

Guides & scripts that use timer_device

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

Build your own lesson with timer_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 →