JuiceBox / Quick Start (Pro) Free guide → / Asset Store

JuiceBox

Quick Start Guide
Pro Edition

01 Wait Effect and Yoyo

This guide assumes you have completed the free Quick Start Guide and are comfortable creating sequences, adding effects, connecting HookNodes, and using the DelegatePicker. Every section here builds on that foundation.

Wait

A Wait effect holds the current value for a period of time. Nothing moves: the sequence pauses at whatever value the previous effect left off at, then advances to the next effect in the chain. Use it to insert delays, hold poses, or wait for an external condition.

Example: Fade In, Hold, Fade Out with a Wait

This achieves the same result as the three-Tween chain from the free guide, but more cleanly: no need for a "target equals current value" trick to hold steady.

  1. Create a Float sequence called FadeInOut with OnUpdate bound to your fade method.
  2. Add a Tween. Bind GetTargetValue to your fullyVisible field and set Duration to 2.
  3. Right-click the filmstrip to the right of the Tween and choose Add Effect > Wait.
  4. The Wait node appears. Set the end condition to Time and enter 1 second.
  5. Add a final Tween. Bind GetTargetValue to fullyHidden and set Duration to 2.

The sequence fades in over 2 seconds, holds for 1 second, then fades out over 2 seconds. The Wait simply pauses the chain: the value stays at 1.0 the entire time.

Wait End Conditions

Wait supports the same end conditions as Follow:

Yoyo

JuiceBox has two forms of yoyo behavior. A Yoyo node sits at the end of the filmstrip (like a Loop node) and bounces the entire sequence: all effects play forward, then all effects play backward, alternating indefinitely. A Yoyo signal is set on an individual effect and bounces only that effect: the effect plays forward then backward before the sequence advances to the next effect in the chain.

Example: A Breathing Scale (Yoyo Node)

  1. Create a Vector3 sequence called Breathe with OnUpdate bound to Transform.localScale.
  2. Add a Tween targeting a slightly enlarged scale (e.g. 1.1, 1.1, 1.1) with Duration 1 and easing SineInOut.
  3. Right-click the filmstrip and choose Add Effect > Yoyo.

The object scales up over 1 second, then the Yoyo node plays the entire chain backward (scaling back down), then forward again. The result is a smooth breathing loop with no visible seam: unlike a regular Loop, there is no pop when the value resets.

Use a Yoyo node when the whole sequence should ping-pong. Use a Yoyo signal on one effect when only that effect should bounce, such as a quick shake in the middle of a longer chain.

02 Swing

A Swing effect oscillates like a pendulum or a price tag hanging from a shelf. The rest value is where the swing comes to rest: the equilibrium position at the bottom of the arc. The value swings back and forth past this point, with each pass losing energy until it settles. The result is natural oscillating motion driven by spring physics.

How Swing Differs from Follow and Shake

Follow chases a target at a constant speed with velocity drag: it moves toward the target and stops. Swing oscillates around a rest point: it passes through the rest value repeatedly, swinging wider or narrower depending on the spring parameters. Shake also oscillates, but around the starting value using a fixed periodic waveform (sine, triangle, etc.). Swing's oscillation is physically simulated using spring dynamics.

Example: A Swinging Sign

Suppose you want a hanging sign to swing when disturbed:

  1. Create a Quaternion sequence called SignSwing with OnUpdate bound to Transform.localRotation.
  2. Right-click the filmstrip and choose Add Effect > Swing.
  3. The Swing node appears with a RestValue slot: this is where the swing settles when it stops. Bind it to a field holding the sign's resting rotation.
  4. Set the end condition to InRange with a range of 0.01 and velocity of 0.1 so the effect ends once the sign has settled.

Give the sign a starting rotation offset from its rest position (via SetStartingValue or a previous effect in the chain), and it swings back and forth around the rest rotation like a pendulum, each pass smaller than the last until it comes to rest.

Smoothing Parameters

A Swing effect always has a SmoothingNode connected (via the Smoothing port on the node header). The SmoothingNode controls the swing's behavior with two fields:

Example: Adjusting the Swing

  1. Connect a SmoothingNode to the Swing's Smoothing header port.
  2. Set Frequency to 4 and Damping to 0.3 for a pendulum-like swing that settles in about a second.
  3. Try Damping at 1.0: the sign glides smoothly to rest with no overshoot.
  4. Try Damping at 0.1: the sign swings back and forth many times before settling.

03 CurveNode: Custom Easing

The built-in easing functions (SineIn, Pow2Out, etc.) cover many cases, but sometimes you need a custom shape: a specific bounce pattern, a staircase effect, or a curve tweaked by hand. A CurveNode lets you wire a Unity AnimationCurve to any Tween's easing slot.

Example: A Custom Bounce Landing

  1. Set up a Vector3 Tween that moves an object downward (e.g. from above a platform to the platform surface) over 1 second.
  2. Right-click the canvas near the Tween node and choose Control Nodes > Curve.
  3. A CurveNode appears. Click it to open the AnimationCurve editor.
  4. Shape your curve: start at (0, 0), overshoot past (0.6, 1.1), dip below to (0.8, 0.95), and settle at (1, 1). This creates a custom squash-and-settle feel.
  5. Drag an edge from the CurveNode's output port to the Tween's Easing input port.

The Tween now follows your hand-drawn curve instead of a mathematical easing function. The curve maps normalized time (0 to 1 on the X axis) to normalized progress (0 to 1 on the Y axis). Values above 1.0 overshoot the target; values below 0.0 undershoot the start.

You can connect the same CurveNode to multiple Tweens if they should share the same easing shape. Disconnecting the CurveNode reverts the Tween to its built-in easing selection.

04 Arc Movement

By default, Tweens and Follows interpolate in a straight line between the start value and the target. Arc movement changes this to a curved path: a spherical interpolation (slerp) that sweeps through an arc rather than cutting across directly. This produces natural-looking motion for objects moving between points in 2D or 3D space.

Arc movement is available on Vector2, Vector3, and Vector4 sequences. It is always on for Quaternion sequences (rotation interpolation is inherently spherical). It is not available for Float sequences.

Example: A Projectile Arc

  1. Create a Vector3 sequence called Lob with OnUpdate bound to Transform.localPosition.
  2. Add a Tween targeting the landing position with a Duration of 1.5.
  3. On the filmstrip's left cap, click the Arc toggle to enable arc movement for the sequence.

The object now travels along a curved arc from its starting position to the target instead of moving in a straight line. The arc is computed using spherical interpolation, so the path sweeps outward from the origin like a ball being lobbed.

Arc movement applies to every Tween and Follow in the sequence: it is a per-sequence setting, not per-effect. All effects in the chain use the same interpolation mode.

Arc movement pairs well with easing. A Pow2Out on an arc tween produces a natural throw-and-land feel: fast at launch, decelerating into the landing.

05 ValueNodes

A ValueNode provides a constant value to an effect's input slot directly in the graph, without writing a script. This is most useful for delegate slots like GetTargetValue: instead of creating a MonoBehaviour with a public field just to hold a target position, you define the value right in the graph editor.

Example: Setting a Target Position Without Code

  1. Create a Vector3 sequence with a Tween effect. You want it to move to a specific position, but you don't want to write a script just to hold that position.
  2. Right-click a pocket below the filmstrip and choose Control Nodes > Value.
  3. A ValueNode appears. Set its type to Vector3 and enter your target coordinates (e.g. 2, 5, 0).
  4. Drag an edge from the ValueNode's output port to the Tween node's GetTarget input port.

The Tween now reads its target from the ValueNode. No script, no public field, no delegate binding: the value lives entirely in the graph. You can adjust it visually at any time.

Twinned ValueNodes

If you want multiple effects to share the same value (for example, three Tweens on different sequences that should all move to the same position), you create twinned ValueNodes. Twinned nodes share the same source: editing one changes them all.

  1. Wire a ValueNode to the first Tween's GetTarget port.
  2. On the ValueNode, click the copy button (next to the close button). A new twinned ValueNode appears.
  3. Wire the twin to the second Tween's GetTarget port. Repeat for the third.

All three ValueNodes display the same value. Change any one and the others update to match: they are linked to the same underlying source.

ValueNodes store their value in the animation data: no MonoBehaviour or script field required. They are the quickest way to parameterize an effect when you don't need the value to come from code at runtime.

06 RandomNode

A RandomNode provides a random value to any numeric input slot on an effect. It replaces the delegate on that slot with a random function: either a one-shot value evaluated when the effect starts, or a continuous noise signal evaluated every frame.

Discrete Random Modes

ModeTypeWhat it produces
FloatRangeFloatA random number between min and max
V2Range / V3Range / V4RangeVectorPer-component random between min and max
OnUnitCircleVector2A random point on the unit circle
InsideUnitCircleVector2A random point inside the unit circle
OnUnitSphere / InsideUnitSphereVector3A random point on or inside the unit sphere
Rotation / RotationUniformQuaternionA random rotation

Noise Modes

ModeTypeWhat it produces
PerlinFloat / V2 / V3Float, VectorSmooth Perlin noise, remapped to your min/max range
SimplexFloat / V2 / V3Float, VectorSimplex noise with optional fBM octaves for richer variation

Noise modes are always evaluated per-frame (the once/per-frame toggle is locked to per-frame). They read Time.time internally and produce smooth, continuous variation. The frequency parameter controls how fast the noise changes. Each node gets a unique seed so multiple RandomNodes produce uncorrelated patterns.

Example: Fireflies with Random Targets

  1. Create a Vector3 sequence called Firefly with OnUpdate bound to Transform.localPosition. Add a Follow effect with Speed 2 and end condition Value in Range at 0.3. Add a Loop node so it repeats.
  2. Right-click a pocket near the Follow node and choose Control Nodes > Random.
  3. Drag an edge from the RandomNode's output to the Follow's GetTargetValue port.
  4. The RandomPicker opens. Select V3Range. Set the min/max for each axis to define the volume the firefly wanders in (e.g. X: -3 to 3, Y: 1 to 4, Z: -3 to 3).
  5. Leave the once/per-frame toggle on Once (the default). Each time the Follow effect starts a new loop iteration, it picks a fresh random target position.

The firefly now chases a new random position each loop, producing organic wandering movement.

Example: Flickering Light with Perlin Noise

  1. Create a Float sequence called Flicker with OnUpdate bound to a method that sets a light's intensity.
  2. Add a Follow effect with a high Speed (e.g. 20) and end condition Time at 999 seconds (effectively infinite).
  3. Add a RandomNode and wire it to GetTargetValue.
  4. In the RandomPicker, select PerlinFloat. Set frequency to 3, min to 0.6, max to 1.0.

The Follow continuously chases a smooth, drifting Perlin noise value. The light intensity wanders between 0.6 and 1.0, producing a natural flicker.

Discrete modes with once-per-start evaluation are ideal for values like random Tween targets or random durations. Noise modes with per-frame evaluation are ideal for continuous organic variation like wind, flickering, or idle sway.

07 Parameter Bindings

Every numeric field on an effect (Speed, Duration, end condition thresholds, smoothing frequency, smoothing damping) can be bound to a delegate. The delegate can be evaluated once when the effect starts, or every frame while the effect runs. This distinction is the parameter binding's evaluation mode.

Once vs. Per-Frame

Example: Speed That Changes Mid-Effect

Suppose a Follow should accelerate as it gets closer to the target:

public class DynamicSpeed : MonoBehaviour
{
    public Transform target;

    public float GetChaseSpeed()
    {
        float dist = Vector3.Distance(transform.position, target.position);
        return Mathf.Lerp(10f, 2f, Mathf.InverseLerp(20f, 1f, dist));
    }
}
  1. On the Follow effect node, click the Speed slot and bind it to DynamicSpeed > GetChaseSpeed.
  2. In the DelegatePicker, toggle the evaluation mode from Once to Per-Frame.

Now the Follow reads a new speed value every frame. When the object is far from the target, it moves fast (speed 10). As it gets close, it slows to speed 2.

Which Parameters Default to What

Most parameters default to EvalOnce. GetTargetValue on a Tween evaluates once (the target is fixed for the Tween's duration). GetTargetValue on a Follow evaluates per-frame (so it can chase a moving target). You can override either default in the DelegatePicker.

Per-frame evaluation has a small runtime cost per parameter per frame. For parameters that genuinely don't change during an effect (like a Tween's Duration), leave them on Once.

08 Timescale

Every effect has a Timescale input that multiplies the passage of time for that effect. A timescale of 2.0 makes the effect run at double speed. A timescale of 0.5 runs at half speed. A timescale of 0.0 pauses the effect entirely: time stops but the effect remains active.

Negative timescale values play the effect backward. A Tween with timescale -1.0 interpolates from the target back toward the start. This lets you reverse individual effects without restructuring your sequence.

Example: Slow-Motion Hit Reaction

Write a script that controls the timescale:

public class HitReaction : MonoBehaviour
{
    private float _timescale = 1f;

    public float GetTimescale() => _timescale;

    public void TriggerSlowMotion()
    {
        _timescale = 0.2f;
        Invoke(nameof(RestoreSpeed), 0.5f);
    }

    private void RestoreSpeed() => _timescale = 1f;
}
  1. On the effect node, find the TimescaleInput port (a green input on the left side).
  2. Drag an edge from the port into empty space to create a HookNode. Bind it to HitReaction > GetTimescale.
  3. In the DelegatePicker, set evaluation to Per-Frame so the timescale updates in real time.

When TriggerSlowMotion() is called, the effect drops to 20% speed for half a second, then returns to normal. Only this specific effect is slowed: other effects in the sequence and other sequences on the object continue at full speed.

Example: A Reversing Tween

public class DirectionControl : MonoBehaviour
{
    public float direction = 1f; // 1 = forward, -1 = backward
    public float GetDirection() => direction;
}

Bind the Tween's TimescaleInput to GetDirection. Setting direction = -1f at runtime makes the Tween play backward from its current position.

Timescale stacks with Unity's Time.timeScale. If Unity's global timescale is 0.5 and the effect's timescale is 0.5, the effect runs at 25% real-time speed. Use global timescale for game-wide pause/slow-motion and per-effect timescale for animation-specific speed control.

09 Runtime Outputs and Linked ValueNodes

Normally a sequence computes values internally and writes them out through OnUpdate. Runtime outputs let you read the sequence's current animated value from the graph and feed it into other systems, without OnUpdate being involved.

The filmstrip's left cap has output ports for the sequence's live values: Target, Result, Velocity, and Timescale. You can wire a ValueNode to these ports to enter linked mode: the ValueNode becomes a live reader of the sequence's runtime state.

Example: Driving a Particle System from a Sequence

Suppose you have a Float sequence animating an intensity value and you want a particle system's emission rate to follow the same curve:

public class ParticleBridge : MonoBehaviour
{
    public ParticleSystem particles;

    public void SetEmissionRate(float rate)
    {
        var emission = particles.emission;
        emission.rateOverTime = rate * 50f;
    }
}
  1. On the filmstrip's left cap, find the Result output port.
  2. Drag an edge from the Result port into empty space. A linked ValueNode appears.
  3. The ValueNode now shows the sequence's current result value in real time during playback.
  4. You can read this value from script, or use it to chain into other graph editor systems.

A linked ValueNode in this mode is read-only: it reflects the sequence's live state rather than providing a constant. It updates every frame while the sequence is running.

Runtime outputs are especially useful for debugging. Wire a linked ValueNode to a sequence's Result output to watch the animated value change in real time during Play mode, right in the graph editor.

10 OnComplete and Sequence Signals

Individual effects have OnStart and OnDone signals (covered in the free guide). Sequences have a third signal: OnComplete. It fires once when the entire sequence finishes: after the last effect in the chain completes and no loop sends it back to the beginning.

Example: Chaining Sequences

Suppose you want a "bounce in" animation followed by an "idle wobble" animation, and they are separate sequences because they have different loop behavior (bounce-in plays once, idle wobble loops forever).

  1. On the BounceIn filmstrip's left cap, find the OnComplete output port.
  2. Drag an edge from OnComplete into empty space. A HookNode appears.
  3. In the DelegatePicker, find StandardFunctions > StartSequence and select it. Enter IdleWobble as the sequence name parameter.

When BounceIn finishes its last effect, OnComplete fires and starts the IdleWobble sequence, no script code required. The two sequences are decoupled and you can modify either one independently.

OnComplete does not fire on looping sequences (the sequence never "completes"). It only fires when the chain reaches its natural end.

You can wire OnComplete from one sequence and OnStart/OnDone from individual effects to build complex multi-sequence choreography entirely in the graph editor, with no script code at all.

11 Live Preview

Live Preview lets you play your animation directly in the Scene view without entering Play mode. It runs the full animation pipeline (all effects, easing, smoothing, looping, and signals) at real speed, showing you exactly what the animation will look like at runtime.

Example: Previewing a Tween

  1. Select a GameObject with a JuiceBoxAnimation that has at least one complete sequence (OnUpdate wired, effects configured).
  2. In the graph editor toolbar, click the Play button (▶).
  3. The animation begins playing in the Scene view. The graph editor highlights the currently active effect in the filmstrip.
  4. Click the Stop button (■) to end the preview. The GameObject returns to its original state.

Live Preview runs in editor time, not game time. Time.timeScale has no effect: the preview always runs at real-world speed. All sequences on the selected animation play simultaneously, just as they would at runtime.

Changes you make to effect parameters while the preview is running are picked up immediately: you can adjust a Duration or easing curve and see the result without restarting.

Live Preview works best with scenes that are fully loaded in the editor. Scenes that load assets dynamically from an asset database at runtime (e.g. Addressables or custom asset pipelines) may not preview correctly, since those assets are not available outside of Play mode.

Live Preview records the GameObject's state before playing and restores it when you stop. Your scene won't be modified by previewing. If something looks wrong, just hit Stop and adjust.

12 Animation Definitions

By default, a JuiceBoxAnimation stores its sequence data directly on the component. An Animation Definition is a ScriptableObject asset that holds that data externally. When multiple GameObjects share the same animation setup (enemies with identical attack patterns, UI buttons with the same hover effect), a definition lets them all point to one shared asset instead of each carrying a duplicate copy.

Creating a Definition

  1. Select the GameObject whose animation you want to share.
  2. In the Inspector, click Create Definition From This Animation.
  3. Choose a save location and filename. A .asset file is created.

The animation data moves from the component to the new asset. The component now shows a Definition field pointing to the asset, and the inline data is cleared. Any other JuiceBoxAnimation components in the scene that had identical sequence names are automatically linked to the same definition.

How Linking Works

When a definition is linked, the graph data lives on the asset. Opening the graph editor for any linked component shows the same sequences: edits to one update all of them. The component itself is lightweight: it holds only the reference to the definition and any per-instance runtime state.

You can also link a definition manually by dragging the asset into the Definition field on a JuiceBoxAnimation component.

Unlinking a Definition

To go back to inline data, click Unlink Definition in the Inspector. This copies the definition's data back onto the component and removes the link. The component becomes independent again: edits to it no longer affect the definition or other linked components.

Asset GUID

When a definition is linked, the Inspector shows the asset's GUID below the Definition field. This is the unique identifier Unity uses to track the asset. It is read-only and exists for debugging: if a definition asset is moved or renamed, Unity tracks it by GUID rather than by file path.

Definitions are ideal for prefabs. Link a definition to the prefab's JuiceBoxAnimation, and every instance of the prefab shares the same animation data without duplicating it into each prefab instance.

13 Look At: Following a Target

The built-in OnUpdate setters cover position, scale, and color. Rotating one object to face another is different: it is a cross-object operation that needs both the rotating object and the thing it looks at, and that target reference lives in your game, not in the graph. You supply it with a short method you bind as the effect's target.

Example: Eyes That Follow the Player

Put a small script on the eye object with a reference to what it tracks:

using UnityEngine;

public class LookAtTarget : MonoBehaviour
{
    public Transform target;

    public Quaternion FaceTarget()
    {
        // Direction from the eye to the thing it is watching, in world space.
        Vector3 toTarget = target.position - transform.position;
        // When the target sits exactly on the eye the direction is the zero
        // vector, which has no orientation. LookRotation cannot build a rotation
        // from it: Unity logs an error and the result is garbage. Keep the
        // current rotation until there is a real direction to face.
        if (toTarget.sqrMagnitude < 0.0001f) return transform.rotation;
        // Build the world rotation that aims the object's forward (+Z) down that direction.
        return Quaternion.LookRotation(toTarget);
    }
}
  1. Create a Quaternion sequence called Gaze with OnUpdate bound to Transform.rotation (world rotation, since LookRotation returns a world-space orientation).
  2. Add a Follow effect. Bind its target to the FaceTarget method on your LookAtTarget component.
  3. Set the Follow's Speed for how quickly the eyes catch up.

The Follow reads FaceTarget every frame, so as the target moves the eyes rotate to track it. The lag is the whole point: a low Speed gives a slow, creepy drift as the eyes lazily swing toward the player. If you wanted the eyes to snap exactly onto the target with no delay, you would not use an animation at all, you would just set the rotation in your own script. The reason to wire up a sequence is to shape the delay.

For a twitchier, more lifelike watch, swap the Follow for a quick Tween toward FaceTarget followed by a short Wait (around 0.2 seconds), looped. The eyes snap to the player, pause, then re-aim, instead of gliding continuously. A faster, smoother stalk and a sharp snap-and-hold read as creepy in different ways; pick the one that fits.

For a 2D sprite, rotate only around Z: compute the angle with Mathf.Atan2 and return Quaternion.Euler(0, 0, angle).

The method does not have to live on the eye object. If it lives elsewhere, write it as a static method whose first parameter is a GameObject; JuiceBox passes the bound object in automatically (the same convention StandardFunctions uses).