# Reactivity: Core

## Scopes

Vide code can run in one of two scopes: [STABLE](https://centau.github.io/vide/api/reactivity-core#Scopes) or [REACTIVE](https://centau.github.io/vide/api/reactivity-core#Scopes).

  * Reactive scopes rerun if a source read within updates.
  * Stable scopes never rerun.
  * Reactive scopes cannot be created directly within another reactive scope.
  * When a scope is destroyed, all scopes created within are also destroyed.

Different functions in Vide's API will run code in different scopes.

WARNING

Yielding is not allowed in any stable or reactive scope. Strict mode will check for this.

## root() [STABLE](https://centau.github.io/vide/api/reactivity-core#Scopes)

Runs a function in a new stable scope.

  * **Type**

luau

        function root<T...>(fn: (Destructor) -> T...): (Destructor, T...)

        type Destructor = () -> ()

  * **Details**

Returns a destructor and any values returned by the callback.

## source()

Creates a new source.

  * **Type**

luau

        function source<T>(value: T): Source<T>

        type Source<T> =
            () -> T -- get
            & (T) -> () -- set

  * **Details**

Call the returned source with no argument to read its value. Call the returned source with an argument to set its value.

  * **Example**

luau

        local count = source(0)
        print(count())-- 0
        count(count() + 1)
        print(count()) -- 1

## effect() [REACTIVE](https://centau.github.io/vide/api/reactivity-core#Scopes)

Runs a function in a new reactive scope.

  * **Type**

luau

        function effect(fn: () -> ())

  * **Details**

The function is ran once immediately.

  * **Example**

luau

        local count = source(1)

        effect(function()
            print(count())
        end)

        -- prints 1

        count(2)

        -- prints 2

## derive() [REACTIVE](https://centau.github.io/vide/api/reactivity-core#Scopes)

Runs a function in a new reactive scope to compute a value for new source.

  * **Type**

luau

        function derive<T>(fn: () -> T): () -> T

  * **Details**

Anytime the reactive scope reruns, the output source value is set to what is returned.

The function is ran once immediately.

  * **Example**

luau

        local count = source(0)
        local text = derive(function() return `count: {count()}` end)

        print(text()) -- "count: 0"

        count(1)

        print(text()) -- "count: 1"

A `derive()` should be used instead of a pure function when you expect it to be read multiple times between updates, because `derive()` will cache the result to prevent recomputing it on every read.

Pure FunctionDerived Source

luau

        local count = source(0)

        local text = function()
            print "ran"
            return `count: {count()}`
        end

        count(1)
        print(text()) -- prints "ran" followed by "count: 1"
        print(text()) -- prints "ran" followed by "count: 1"

luau

        local count = source(0)

        local text = derive(function()
            print "ran"
            return `count: {count()}`
        end)

        count(1) -- prints "ran"
        print(text()) -- prints "count: 1"
        print(text()) -- prints "count: 1"


# Dynamic Scopes

Eventually you may need a way to dynamically create and destroy UI elements resulting from source updates. Vide provides functions to help you do this, known as _dynamic scope_ functions.

These functions create and destroy scopes for you in response to source updates. They return a source containing the created component. This source can be parented as a child which will update the shown children whenever the source updates.

The simplest example is using `show()`.

luau

    local source = vide.source
    local create = vide.create
    local show = vide.show
    local root = vide.root

    function Button(props: { Text: string, Activated: () -> () })
        return create "TextButton" {
            Text = props.Text,
            Activated = props.Activated
    end

    function Menu()
        return create "TextLabel" {
            Text = "This is a menu"
    end

    function App()
        local toggled = source(false)

        return create "ScreenGui" {
            Button {
                Text = "Toggle Menu",
                Activated = function()
                    toggled(not toggled())
                end
            },

            show(toggled, Menu)
    end

    root(function()
        App().Parent = game.StarterGui
    end)

This is a complete example of rendering UI which has a single button that toggles the opening of a menu.

Another common function is `indexes()`. This function creates a component for each index in a table.

Each component created is done so in a new and independent stable scope. The indexes of the table are checked each source update to prevent redunant destruction and recreation of UI elements.

luau

    local source = vide.source
    local create = vide.create
    local indexes = vide.indexes
    local root = vide.root

    local function Todo(props: {
        Text: () -> string,
        Position: number,
        Activated: () -> ()
    })
        return create "TextButton" {
            Text = function() return props.Position .. ": " .. props.Text() end,
            LayoutOrder = props.Position,
            Activated = Activated
    end

    local function TodoList(props: { List: () -> Array<string> })
        return create "Frame" {
            create "UIListLayout" {},

            indexes(props.List, function(text, i)
                return Todo {
                    Text = text,
                    Position = i,
                    Activated = function() -- remove the todo when clicked
                        local list = props.List()
                        table.remove(list, i)
                        props.List(list)
                    end
            end)
    end

    function App()
        local list = source {
            "finish the crash course",
            "star Vide's GitHub"

        return create "ScreenGui" {
            TodoList { List = list },
    end

    root(function()
        App().Parent = game.StarterGui
    end)

The reactive graph for the above example:

When you edit a table in a source, you must set that table again to actually update the source.

luau

    local src = source { 1, 2 }
    local data = src()
    table.insert(data, 3) -- no effects will run
    src(data) -- effects will run

All dynamic scope functions also support delaying the destruction of the scope. This is useful for playing any sort of animation or effect before the UI instance is removed.

If you have the following code, for example:

lua

    local function Menu()
        return create "Frame" {}
    end

    local toggled = source(true)

    create "ScreenGui" {
        show(toggled, function()
            return Menu {}
        end)

    toggled(false) -- menu will disappear immediately

lua

    local function Menu(props: { Visible: () -> boolean })
        local transparency = spring(function()
            return if p.Visible then 0 else 1
        end

        return create "Frame" {
            BackgroundTransparency = transparency
    end

    local toggled = source(true)

    create "ScreenGui" {
        show(toggled, function(_, present)
            return Menu { p.Visible = present }, 3 -- give a generous 3 seconds for the spring to complete before destroying
        end)

    toggled(false)
    -- `present` will go `false` immediately
    -- transparency will begin being sprung
    -- after 3 seconds the scope is destroyed, giving the spring enough time to complete

If `toggled` goes from truthy to falsey, beginning the timer, but then back to truthy before the timer finishes, the timer is cancelled and the scope is not destroyed.


# Derived Sources

We have seen the basic way to derive a source:

luau

    local count = source(0)

    local text = function()
        return "count: " .. tostring(count())
    end

    print(text()) -- "count: 0"
    count(1)
    print(text()) -- "count: 1"

However, in some cases where this source could be used by multiple effects at the same time, the function wrapping the source will needlessly rerun to convert the count into a string for each effect using it.

luau

    local source = vide.source
    local effect = vide.effect

    local count = source(0)

    local text = function()
        print "ran"
        return "count: " .. tostring(count())
    end

    effect(function() text() end)
    effect(function() text() end)

    count(1) -- prints "ran" x2

To avoid this, you can use `derive()` to derive a new source instead. This will run a function in a reactive scope only when a source used inside updated. Reading this derived source multiple times will just return a cached result.

luau

    local source = vide.source
    local effect = vide.effect
    local derive = vide.derive

    local count = source(0)

    local text = derive(function()
        print "ran"
        return "count: " .. tostring(count())
    end)

    effect(function() text() end)
    effect(function() text() end)

    count(1) -- prints "ran" x1

Because `derive()` creates a reactive scope, it must be called within a stable scope, just like `effect()`.

If the recalculated value is the same as the old value, the derived source will not rerun the effects using it.

The reactive graph for the above example:

Deriving a source in this manner is similar to creating an effect to update another source. You should avoid doing this using an effect however. Improper usage could accidently create infinite loops in the reactive graph. Always favour deriving when you need one source to update based on another source.


# Strict Mode

While developing UI with Vide, you should use Vide's strict mode, which can be set with `vide.strict = true` once when you first require Vide. Strict mode will add extra safety checks and emit better error traces, particularly when errors occur in property bindings.

Strict mode is automatically enabled when Vide is required in O0 or O1 optimization (default studio level). You can `vide.strict = false` if you do not want this.

Strict mode will run derived sources and effects twice each time they update. This is to help ensure that derived source computations are pure, and that any cleanups made in derived sources or effects are done properly.

luau

    local source = vide.source
    local effect = vide.effect

    vide.strict = true

    local count = source(0)

    local ran = 0
    effect(function()
        count()
        ran += 1
    end)

    print(ran) -- 2
    count(1)
    print(ran) -- 4

A full list of what strict mode will do can be found [here](https://centau.github.io/vide/api/strict-mode.html).


# Implicit Effects

Explicitly creating effects to update properties is tedious. You can _implicitly_ create an effect to update properties instead.

Implicit EffectExplicit Effect

luau

    local create = vide.create
    local source = vide.source

    local function Counter()
        local count = source(0)

        return create "TextButton" {
            Activated = function()
                count(count() + 1)
            end,

            Text = function()
                return "count: " .. count()
            end
    end

luau

    local create = vide.create
    local source = vide.source
    local effect = vide.effect

    local function Counter()
        local count = source(0)

        local instance = create "TextButton" {
            Activated = function()
                count(count() + 1)
            end

        effect(function()
            instance.Text = "count: " .. count()
        end)

        return instance
    end

This example is equivalent to the example seen on the previous page.

Instead of explicitly creating an effect, assigning a (non-event) property a function will implicitly create an effect to update that property.

## Children

Children can also be set in a similar manner. A source passed as a child (passed with a number key instead of string key) can return an instance or an array of instances. An effect is automatically created to unparent removed instances and parent new instances on source update.

luau

    local items = source {
        create "TextLabel" { Text = "A" }

    local function List(props: { children: () -> { Instance } })
        return create "Frame" {
            create "UIListLayout" {},
            props.children
    end

    local list = List { children = items } -- creates a list with text label "A"

    items {
        create "TextLabel" { Text = "B" },
        create "TextLabel" { Text = "C" }

    -- this will automatically unparent text label "A", and parent labels "B" and "C"


# Concepts Summary

A summary of all the concepts covered during the crash course.

## Source

A source of data.

Stores a single value that can be updated.

Created with `source()`.

## Derived Source

A new source composed of other sources.

Created with a plain function or with `derive()`.

## Effect

Anything that happens in response to a source update.

Created with `effect()`.

## Stable Scope

One of the two types of Vide scopes.

Created by:

  * `root()`
  * `untrack()`
  * `show()`
  * `indexes()`

Stable scopes do not track sources and never rerun.

New stable or reactive scopes can be created within a stable scope.

## Reactive Scope

Created by:

  * `effect()`
  * `derive()`

Reactive scopes do track sources and will rerun when those sources update.

Reactive scopes cannot be created within a reactive scope, but stable scopes can be created within a reactive scope.

## Scope Cleanup

When a scope is rerun or destroyed, all scopes created within it are automatically destroyed.

Any functions queued by `cleanup()` are also ran.

## Reactive Graph

The combination of stable and reactive scopes can viewed graphically, called a _reactive graph_. This can be a more intuitive way to think of the relationships between effects and the sources they depend on.

### Code

luau

    local count = source(0)

    root(function()
        local text = derive(function()
            return "count: " .. count()
        end)

        effect(function()
            print(text())
        end)
    end)

### Graph resulting from code

Notes:

  * Since `count` is a source, not an effect, it can exist outside of scopes.
  * An update to `count` will cause `text` to rerun, which then causes `effect` to rerun.
  * When the root scope is destroyed, `text` and `effect` will be destroyed alongside it, since they were created within it. `count` will be untouched and future updates to `count` will have no effect.


# Sources

Sources are special objects that store a single value and are the core of Vide's reactivity.

A source can be created using `source()`.

luau

    local source = vide.source

    local count = source(0)

The value passed to `source()` is the initial value of the source.

The value of a source can be set by calling it with an argument, and can be read by calling it with no arguments.

luau

    count(count() + 1) -- increment count by 1

Sources can be _derived_ by wrapping them in functions.

luau

    local count = source(0)

    local text = function()
        return "count: " .. tostring(count())
    end

    print(text()) -- "count: 0"
    count(1)
    print(text()) -- "count: 1"

While the above can be achieved with plain variables, the use for sources will be obvious in the next part.


# Creating UI

Instances are created using `create()`.

Parentheses `()` can be omitted when calling functions with string or table literals for brevity.

luau

    local create = vide.create

    return create "ScreenGui" {
        create "Frame" {
            AnchorPoint = Vector2.new(0.5, 0.5),
            Position = UDim2.fromScale(0.5, 0.5),
            Size = UDim2.fromScale(0.4, 0.7),

            create "TextLabel" {
                Text = "hi"
            },

            create "TextLabel" {
                Text = "bye"
            },

            create "TextButton" {
                Text = "click me",

                Activated = function()
                    print "clicked!"
                end

Assign a value to a string key to set a property, and assign a value to a number key to set a child. Events can be connected to by assigning a function to a string key.


# Reactive Components

Reactive components in Vide are created using sources and effects - sources to store the data, and effects to display the data.

luau

    local create = vide.create
    local source = vide.source
    local effect = vide.effect

    local function Counter()
        local count = source(0)

        local instance = create "TextButton" {
            Activated = function()
                count(count() + 1)
            end

        effect(function()
            instance.Text = "count: " .. count()
        end)

        return instance
    end

Above is an example of a counter component, that when clicked, will increment its internal count, and automatically update its text to reflect that count.

Each instance of `Counter()` will maintain its own independent count, since the count source is created inside the component.

External sources can also be passed into components for them to use.

luau

    local function CountDisplay(props: { count: () -> number })
        local count = props.count

        local instance = create "TextLabel" {}

        effect(function()
            instance.Text = "count: " .. count()
        end)

        return instance
    end

    local count = source(0)

    CountDisplay {
        count = count

    count(1) -- the CountDisplay component will update to display this count

Sources can be created internally or passed in from externally, there are no restrictions on how they are used as long as the effect using it is created within a stable scope.


# Cleanup

Sometimes you may need to do some cleanup when destroying a component or after a side-effect from a source update. Vide provides a function `cleanup()` which is used to queue a callback for the next time a reactive scope is rerun or destroyed, or when a stable scope is destroyed.

luau

    local root = vide.root
    local source = vide.source
    local effect = vide.effect
    local cleanup = vide.cleanup

    local count = source(0)

    local destroy = root(function()
        effect(function()
            local x = count()
            cleanup(function() print(x) end)
        end)

        cleanup(function() print "root destroyed" end)
    end)

    count(1) -- prints "0"
    count(2) -- prints "1"
    destroy() -- prints "2" and "root destroyed"

TIP

Roblox instances do not need to be explicitly destroyed for their memory to be freed, they only need to be parented to `nil`. So there is no need to use `cleanup()` to destroy instances. However, be wary of connecting a function that references an instance to an event from the same instance, this causes the instance to reference itself and never be freed. In such a case you would need to use `cleanup()` to disconnect this connection or to explicitly destroy the instance.


# Actions

Actions are special callbacks that you can pass along with properties, to run some code on an instance receiving them.

luau

    local action = vide.action

    create "TextLabel" {
        Text = "test",

        action(function(instance)
            print(instance.Text)
        end)

    -- will print "test"

Actions can be wrapped with functions for reuse. Below is an example of an action used to listen for property changes:

luau

    local action = vide.action
    local source = vide.source
    local effect = vide.effect
    local cleanup = vide.cleanup

    local function changed(property: string, callback: (new) -> ())
        return action(function(instance)
            local connection = instance:GetPropertyChangedSignal(property):Connect(function()
                callback(instance[property])
            end)

            -- remember to clean up the connection when the reactive scope the action
            -- is ran in is destroyed, so the instance can be garbage collected
            cleanup(connection)
        end)
    end

    local output = source ""

    local instance = create "TextBox" {
        changed("Text", output)

    effect(function()
        print(output())
    end)

    instance.Text = "foo" -- "foo" will be printed by the effect

The source `output` will be updated with the new property value any time it is changed externally.


# Components

Vide encourages separating different parts of your UI into functions called _components_.

A component is a function that creates and returns a piece of UI.

This is a way to separate your UI into small chunks that you can reuse and put together.

Button.luauMenu.luau

luau

    local create = vide.create

    local function Button(props: {
        Position: UDim2,
        Text: string,
        Activated: () -> ()
    })
        return create "TextButton" {
            BackgroundColor3 = Color3.fromRGB(50, 50, 50),
            TextColor3 = Color3.fromRGB(255, 255, 255),
            Size = UDim2.fromOffset(200, 150),

            Position = props.Position,
            Text = props.Text,
            Activated = props.Activated,

            create "UICorner" {}
    end

    return Button

luau

    local create = vide.create

    local Button = require(Button)

    local function Menu()
        return create "ScreenGui" {
            Button {
                Position = UDim2.fromOffset(200, 200),
                Text = "back",
                Activated = function()
                    print "go to previous page"
                end
            },

            Button {
                Position = UDim2.fromOffset(400, 200),
                Text = "next",
                Activated = function()
                    print "go to next page"
                end
    end

A single parameter `props` is used to pass properties to the component.


# Effects

Effects are functions that are ran in response to source updates. A source and effect is analogous to a signal and connection.

Effects are created using `effect()`.

luau

    local source = vide.source
    local effect = vide.effect

    local count = source(0)

    effect(function()
        print("count: " .. count())
    end)

    -- "count: 0" printed
    count(1)
    -- "count: 1" printed

Any source read inside an effect is tracked and will rerun the effect when that source is updated.

The effect runs its callback once immediately to initially figure out what sources are being read.

Derived sources are also tracked, it does not matter how deeply nested inside a function a source is.

luau

    local source = vide.source
    local effect = vide.effect

    local count = source(1)

    local doubled = function()
        return count() * 2
    end

    effect(function()
        print("doubled count: " .. doubled())
    end)

    -- "doubled count: 2" printed
    count(2)
    -- "doubled count: 4" printed

If a source is updated with the same value it already had, it will not rerun effects depending on it.

You can also read from a source within an effect without the effect tracking it.

luau

    local source = vide.source
    local effect = vide.effect
    local untrack = vide.untrack

    local a = source(0)
    local b = source(0)

    effect(function()
        print(`a: {a()} b: {untrack(b)}`)
    end)

    a(1) -- prints "a: 1 b: 0"
    b(1) -- prints nothing
    a(2) -- prints "a: 2 b: 1"


# Dynamic Scopes

Dynamic scopes are scopes that are created and destroyed in response to source updates. This is needed for conditionally rendering parts of your UI, such as opening and closing menus.

While Vide provides functions for common ways to do this, this section will show how you can implement them yourself so you are not limited by only what is provided.

## Recreating [`show()`](https://centau.github.io/vide/api/reactivity-dynamic.html#show-reactive)

The most basic one, `show()`, can be implemented yourself like so:

luau

    local function show(toggle: () -> unknown, component: () -> Instance)
        return derive(function()
            return if toggle() then untrack(component) else nil
        end)
    end

The main thing to note here is the use of `untrack()`. This function runs its callback in a new stable scope. Without this, if the component were to create a reactive scope, an error would occur since a reactive scope cannot be created within a reactive scope.

You can see from the above graph how the effect would not be created directly inside the derive, there is a stable scope between them. This requirement exists as a guard against unintentional rerendering of UI.

## Recreating [`switch()`](https://centau.github.io/vide/api/reactivity-dynamic.html#switch-reactive)

luau

    local function switch(key)
        return function(map)
            return derive(function()
                local component = map[key()]
                return if component then untrack(component) else nil
            end)
        end
    end

## Recreating [`indexes()`](https://centau.github.io/vide/api/reactivity-dynamic.html#indexes-reactive)

This is a more complicated function because it manages multiple scopes at the same time, unlike the previous functions. Because some scopes may persist between reruns, we cannot use `untrack()` anymore which automatically destroys on rerun; we must use `root()` where the lifetime of each scope is managed manually and independently.

luau

    local function indexes<I, VI, VO>(
        input: () -> Map<I, VI>,
        transform: (value: () -> VI, index: I) -> VO
    )
        local index_caches = {} :: Map<I, {
            previous_input: VI,
            output: VO,
            source: (VI) -> VI,
            destroy: () -> ()
        }?>

        -- destroy all scopes if the parent scope is destroyed
        cleanup(function()
            for _, cache in index_caches do
                assert(cache).destroy()
            end
        end)

        return derive(function()
            local new_input = input()

            -- destroy scopes of removed indexes
            for i, cache in index_caches do
                if new_input[i] == nil then
                    assert(cache).destroy()
                    index_caches[i] = nil
                end
            end

            -- create scopes or update sources of added or changed index values
            for i, v in new_input do
                local cache = index_caches[i]

                if cache == nil then -- no scope created for this index, create one
                    local src = source(v)

                    local destroy, result = root(function()
                        return transform(src, i)
                    end)

                    index_caches[i] = {
                        destroy = destroy,
                        source = src,
                        output = result,
                        previous_input = v
                elseif cache.previous_input ~= v then -- scope exists, update source
                    cache.previous_input = v
                    cache.source(v)
                else -- scope exists and value has not changed; do nothing
                end
            end

            -- return the cached output values as an array
            local array = table.create(#index_caches)

            for _, cache in index_caches do
                table.insert(array, assert(cache).output)
            end

            return array
        end)
    end

Though the above functions are already provided to you by Vide, this serves as an example for how you may create your own dynamic scope functions.


# Scopes

Just like how a signal's connection may need to be disconnected, a source's effect also may need to be disconnected.

But the disconnecting of many signals and connections is tedious and verbose. Vide instead operates on the concept of scopes which provides a much cleaner API, given that you follow a few rules.

There are two types of scopes: stable and reactive.

  * A scope must be created within another scope.
  * Stable scopes never rerun.
  * Reactive scopes can rerun.
  * A reactive scope cannot be created within another reactive scope, only within a stable scope.

An exception to the first rule is `root()`, which creates the initial scope that you destroy manually with a destructor function it returns.

`root()` creates a stable scope. `effect()` creates a reactive scope.

Whenever a scope is destroyed, any scope created within that scope is also destroyed, and so on.

luau

    local root = vide.root
    local source = vide.source
    local effect = vide.effect

    local count = source(0)

    local function setup()
        effect(function()
            print(count())
        end)
    end

    setup() -- error, effect() tried to create a reactive scope with no stable scope

    local destroy = root(setup) -- ok since effect() was called in a stable scope

    count(1) -- prints "1"
    count(2) -- prints "2"

    destroy()

    count(3) -- reactive scope created by effect() is destroyed, it does not rerun

Vide's reactivity can be represented graphically, as a _reactive graph_.

The reactive graph for the above example looks like so:

When the stable `root()` scope is destroyed, the reactive `effect()` scope will also be destroyed since it was created within it.

This is important because you may have an effect that updates the property of a UI instance, meaning the effect is referencing and holding that instance in memory. The effect being destroyed will remove this reference, allowing the instance to be garbage collected.

You don't need to worry about ensuring all your effects are created within a stable scope, since you should be creating all your UI and effects within a single top-level `root()` call that puts all your UI together, making it safe to assume any effect created will be created under this stable scope.


# Reactivity: Dynamic Scopes

Dynamic scopes are scopes that are created or destroyed in response to source updates. Vide provides functions for some common use-cases for dynamic scopes.

## show() [REACTIVE](https://centau.github.io/vide/api/reactivity-core#Scopes)

Shows a component if the source is truthy. Optionally shows a fallback component if the source is falsey.

  * **Type**

luau

        function show<T>(source: () -> unknown, component: Constructor<T>): () -> T?
        function show<T, U>(source: () -> unknown, component: Constructor<T>, fallback: () -> U): () -> T | U

        type Constructor<T> = () -> (T, number?)

  * **Details**

Creates a reactive scope internally to detect source updates.

The component is run in a stable scope when truthy, otherwise the stable scope is destroyed.

Returns a source holding an instance of the currently shown component or `nil` if no component is currently shown.

Destruction of the scope can be delayed by returning the number of seconds to delay by, after the component.

## switch() [REACTIVE](https://centau.github.io/vide/api/reactivity-core#Scopes)

Shows one of a set of components depending on a source and a mapping table.

  * **Type**

luau

        function switch<K, V>(source: () -> K): (map: Map<K, Constructor<V>>): () -> V?

        type Constructor<T> = () -> (T, number?)

  * **Details**

Creates a reactive scope internally to detect source updates.

When the source updates, its value is inputted into a map to get a component constructor. This component is then run in a stable scope. The previous stable scope is destroyed.

Returns a source holding an instance of the currently shown component or `nil` if no component is currently shown.

Destruction of the scope can be delayed by returning the number of seconds to delay by, after the component.

  * **Example**

luau

        local logged = source(false)

        local button = switch(logged) {
            [true] = function()
                return Button { Text = "Log out", Toggle = logged }
            end,

            [false] = function()
                return Button { Text = "Log in",  Toggle = logged }
            end

## indexes() [REACTIVE](https://centau.github.io/vide/api/reactivity-core#Scopes)

Shows a component for each index in a table.

  * **Type**

luau

        function indexes<KI, VI, VO>(
            source: () -> Map<KI, VI>,
            constructor: (value: () -> VI, index: KI) -> (VO, number?)
        ): Array<VO>

  * **Details**

Creates a reactive scope internally to detect source updates.

When the source table updates, a component is generated for each index in the table.

    * For any added index, the `constructor` function is run in a new stable scope to produce an instance that is cached.
    * For any removed index, the stable scope for that index is destroyed.

The `constructor` function is called with:

    1. A _source containing the index's value_.
    2. The _index itself_.

Anytime an existing index's value changes, the `constructor` function is not rerun, instead, that index's corresponding source is updated with the new value.

Returns a source holding an array of instances currently shown.

Destruction of the scope can be delayed by returning the number of seconds to delay by, after the component.

  * **Example**

luau

        type Item = {
            name: string,
            icon: number

        local items = source {} :: () -> Array<Item>

        local displays = indexes(items, function(item, i)
            return ItemDisplay {
                Name = function()
                    return i .. ": " .. item().name
                end,

                Image = function()
                    return "rbxassetid://" .. item().icon
                end,
        end)

## values() [REACTIVE](https://centau.github.io/vide/api/reactivity-core#Scopes)

Shows a component for each value in a table.

  * **Type**

luau

        function values<KI, VI, VO>(
            source: () -> Map<KI, VI>,
            constructor: (value: VI, index: () -> KI) -> (VO, number?)
        ): Array<VO>

  * **Details**

Operates with the same idea as `indexes()`, but applied to values instead of indexes.

Creates a reactive scope internally to detect source updates.

When the source table updates, a component is generated for each value in the table.

    * For any added value, the `constructor` function is run in a new stable scope to produce an instance that is cached.
    * For any removed value, the stable scope for that value is destroyed.

The `constructor` function is called with:

    1. The _value itself_.
    2. A _source containing the value's index_.

Anytime an existing value's index changes, the `constructor` function is not rerun, instead, that value's corresponding source is updated with the new index.

Returns a source holding an array of instances currently shown.

Destruction of the scope can be delayed by returning the number of seconds to delay by, after the component.

WARNING

Having the same values appear multiple times in the input source table can cause unexpected behavior. Strict mode has checks for this.

  * **Example**

luau

        type Item = {
            name: string,
            icon: number

        local items = source {} :: () -> Array<Item>

        local displays = values(items, function(item, i)
            return ItemDisplay {
                Name = function()
                    return i() .. ": " .. item.Name
                end

                Image = "rbxassetid://" .. item.icon,
        end)

  * **Extra**

When should you use `indexes()` and `values()`?

`values()` should be used when you have a fixed set of objects where the same objects can be re-arranged in the source table. It maps a value to a UI element.

e.g.

    * List of all players.
    * Inventory of items.
    * Chat message history.
    * Toast notifications.

`indexes()` should be used in other cases, especially when your source table has primitive values. It maps an index to a UI element.

e.g.

    * List of character or weapon stats.

In most cases, both functions will produce the same observed result. The main difference is performance, picking the right function to use can result in less property updates and less re-renders. One case to note is that `values()` works nicely when animating re-ordering of instances, since the source index can be used to animate a change in position for the UI element.


# Animation

## spring() [REACTIVE](https://centau.github.io/vide/api/reactivity-core#Scopes)

Returns a new source with a value always moving torwards the input source value.

  * **Type**

luau

        function spring<T>(
            source: () -> T & Animatable,
            period: number = 1,
            damping_ratio: number = 1
        ): (() -> T, SpringControl<T>)

        type Animatable = number | CFrame | Color3 | UDim | UDim2 | Vector2 | Vector3 | Rect

        type SpringControl<T> = ({
            position: T?,
            velocity: T?,
            impulse: T?
        }) -> ()

  * **Details**

Creates a reactive scope internally to detect source updates.

The movement is physically simulated according to a [spring](https://en.wikipedia.org/wiki/Simple_harmonic_motion).

`period` is the amount of time in seconds it takes for the spring to complete one full cycle if undamped.

`damping_ratio` is the amount of resistance applied to the spring.

    * >1 = Overdamped - slowly reaches target without any overshoot.
    * 1 = Critically damped - reaches target without any overshoot.
    * <1 = Underdamped - reaches target with some overshoot.
    * 0 = Undamped - never stabilizes, oscillates forever.

By default, the spring solver is ran at 120 Hz in [`Heartbeat`](https://create.roblox.com/docs/reference/engine/classes/RunService#Heartbeat). You can change when the solver runs by calling `vide.step(dt)`, which will advance the simulation time by `dt` seconds and automatically stop the solver running in heartbeat.

WARNING

Large periods or damping ratios can break the spring.


# Element Creation

## create()

Creates a new UI element, applying any given properties.

  * **Type**

luau

        function create(class: string): (Properties) -> Instance
        function create(instance: Instance): (Properties) -> Instance

        type Properties = Map<string|number, unknown>

  * **Details**

The function can take either a `string` or an `Instance` as its first argument.

    * If given a `string`, a new instance with the same class name will be created.
    * If given an `Instance`, a new instance that is a clone of the given instance will be created.

This returns another function that is used to apply any properties to the new instance.

  * **Property setting rules**

    * **index is string:**
      * **value is function:**
        * **property is event:** connect function as callback
        * **property is not event:** create effect to update property
      * **value is not function:** set property to value
    * **index is number:**
      * **value is action:** run action
      * **value is table:** recurse table
      * **value is function:** create effect to update children
      * **value is instance:** set instance as child
  * **Example**

Basic element creation.

luau

        local frame = create "TextButton" {
            Name = "Button",
            Size = UDim2.fromOffset(200, 160),

            Activated = function()
                print "clicked"
            end,

            create "UICorner" {}

## action()

Creates a special object that can be passed to `create()` to invoke custom actions on instances.

  * **Type**

luau

        function action((Instance) -> (), priority: number = 1): Action

  * **Details**

When passed to `create()`, the function is called with the instance being created as the only argument. Actions take precedence over property and child assignments.

A priority can be optionally specified to ensure certain actions run after other actions. Lower priority values are ran first.

  * **Example**

An action to listen to changed properties:

luau

        local function changed(property: string, fn: (new) -> ())
            return action(function(instance)
                local cn = instance:GetPropertyChangedSignal(property):Connect(function()
                    fn(instance[property])
                end)

                -- disconnect on scope destruction to allow gc of instance
                cleanup(function()
                    cn:Disconnect()
                end)
            end)
        end

        local output = source ""

        create "TextBox" {
            -- will update the output source anytime the text property is changed
            changed("Text", output)

## changed()

A wrapper for `action()` to listen for property changes.

  * **Type**

luau

        function changed(property: string, fn: (unknown) -> ()): Action

  * **Details**

Will run the given function immediately and whenever the property updates.

The function is called with the updated property value.

Runs with an action priority of 1.

## mount() [STABLE](https://centau.github.io/vide/api/reactivity-core#Scopes)

Runs a function in a new stable scope and optionally applies its result to a target instance.

  * **Type**

luau

        function mount<T>(component: () -> T, target: Instance?): () -> ()

  * **Details**

This is a utility for `root()` when parenting a component to an existing instance.

The result of the function is applied to a target in the same way properties are using `create()`.

Returns a function that when called will destroy the stable scope.

  * **Example**

luau

        local function App()
            return create "ScreenGui" {
                create "TextLabel" { Text = "Vide" }
        end

        local destroy = mount(App, game.StarterGui)


# Reactivity: Utility

## cleanup()

Queues a callback to run when a scope is reran or destroyed.

  * **Type**

luau

        function cleanup(v: Function | Disconnectable | Destroyable | thread)

        type Function = () -> ()
        type Destroyable = { destroy: () -> () }
        type Disconnectable = { disconnect: () -> () }

  * **Example**

luau

        local count = source(0)

        local destroy = root(function()
            effect(function()
                count()

                cleanup(function()
                    print "cleaned"
                end)
            end)
        end

        -- nothing printed yet
        count(1) -- prints "cleaned"
        count(2) -- prints "cleaned"
        destroy() -- prints "cleaned"

## untrack() [STABLE](https://centau.github.io/vide/api/reactivity-core#Scopes)

Runs a function in a new stable scope.

  * **Type**

luau

        function untrack<T>(source: () -> T): T

  * **Details**

Can be used inside a reactive scope to read from sources you do not want tracked by the reactive scope.

  * **Example**

luau

        local a = source(0)
        local b = source(0)

        local sum = derive(function()
            return a() + untrack(b)
        end)

        print(sum()) -- 0
        b(1) -- untracked so reactive scope created by derive() does not rerun
        print(sum()) -- 0
        a(1) -- reactive scope created by derive() reruns
        print(sum()) -- 2

## read()

Utility used to read a value that is either a primitive or a source.

  * **Type**

luau

        function read<T>(value: T | () -> T): T

## batch()

Runs a function where any source updates made within the function do not trigger effects until after the function ends.

  * **Type**

luau

        function batch(fn: () -> ())

  * **Details**

Improves performance when an effect depends on multiple sources, and those sources need to be updated.

  * **Example**

luau

        local a = source(0)
        local b = source(0)

        effect(function()
            print(a() + b())
        end)

        -- prints "0"

        batch(function()
            a(1) -- no print
            b(2) -- no print
        end)

        -- prints "3"

## context() [STABLE](https://centau.github.io/vide/api/reactivity-core#Scopes)

Creates a new context.

  * **Type**

luau

        function context<T>(default: T): Context<T>

        type Context<T> =
            () -> T -- get
            & <U>(T, () -> U) -> U -- set

  * **Details**

Calling `context()` returns a new context function. Call this function with no arguments to get the context value. Call this function with a value and a function to create a new context with the given value.

The new context is run under a stable scope.

  * **Example**

luau

        local theme = context()

        local function Button()
            print(theme())
        end

        root(function()
            theme("light", function()
                 Button() -- prints "light"

                theme("dark", function()
                    Button() -- prints "dark"
                end)
            end)
        end)


# Strict Mode

Strict mode is library-wide and can get set by doing:

luau

    vide.strict = true

It is automatically enabled when Vide is first required and not running in O2 optimization level.

Strict mode is designed to help the development process by adding safety checks and identifying improper usage.

Currently, strict mode will:

  1. Run reactive scopes twice when a source updates.
  2. Throw an error if yields occur where they are not allowed.
  3. Checks for `indexes()` and `values()` outputting primitive values.
  4. Checks for `values()` input having duplicate values.
  5. Checks for duplicate nested properties at same depth.
  6. Checks for destruction of an active scope.
  7. Better error reporting and stack traces.

By rerunning reactive scopes twice each time they update, it helps ensure that computations are pure, and that any cleanup is done correctly.

Accidental yielding within reactive scopes can break Vide's reactive graph, which strict mode will catch.

As well as additional safety checks, Vide will dedicate extra resources to recording and better emitting stack traces where errors occur, particularly when implicit effects are created for instance property updating.

It is recommended to develop UI with strict mode and to disable it when pushing to production. In Roblox, production code compiles at O2 by default, so you do not need to worry about disabling strict mode unless you have manually enabled it.


# Lyra

    Safe and simple player data management for Roblox

    Documentation »

Lyra makes it easy to safely and robustly manage your game's player data. It's designed to handle large amounts of data, prevent common game-breaking bugs, and make it easy to update your data format without breaking existing saves.

## Early Development

While Lyra has been tested and is used in production, it's still in early development. Some features, like transactions, while tested in controlled environments, have not yet been battle-tested in production at scale.

**Avoid using Lyra in production games where data loss would be catastrophic until it has been tested more thoroughly.**

## Features

- **Transactions** - A powerful tool to implement features like trading, while making bugs like item duplication impossible
- **Session Locking** - Prevents common bugs that lead to corruption and data loss
- **Validation** - Ensures your data is always in a consistent state
- **Auto-Sharding** - Handles large data by automatically splitting across multiple DataStore keys
- **Migrations** - Update your data format without breaking existing saves
- **Drop-in** - Import your existing data and switch over seamlessly

## Quick Start

```lua
local store = Lyra.createPlayerStore({
    name = "PlayerData",
    template = {
        coins = 0,
        inventory = {},
    },
    schema = t.strictInterface({
        coins = t.number,
        inventory = t.table,
    }),
})

-- Load data when players join
Players.PlayerAdded:Connect(function(player)
    store:loadAsync(player)
end)

-- Free up resources when players leave
Players.PlayerRemoving:Connect(function(player)
    store:unloadAsync(player)
end)

-- Update data
store:updateAsync(player, function(data)
    data.coins += 100
    return true
end)

-- Atomic transactions
store:txAsync({player1, player2}, function(state)
    local amount = 50
    state[player1].coins -= amount
    state[player2].coins += amount
    return true
end)

## Installation

Add to your `wally.toml`:
```toml
Lyra = "paradoxum-games/lyra@0.6.0"

Check out the [documentation](https://paradoxum-games.github.io/lyra/) for guides, examples, and API reference.


### Data Safety

Session locking prevents data corruption, atomic transactions enable safe trading, and built-in validation catches bad data before it's saved.

### High Performance

Auto-sharding handles large datasets, efficient DataStore usage minimizes costs, and automatic retries handle service limits gracefully.

### Developer Friendly

Schema migrations let you evolve your data format, drop-in compatibility makes switching easy, and type-safe APIs prevent common mistakes.


# sm64-roblox

A port of Super Mario 64's movement code into Roblox Luau (in `--!strict` mode), hosted as a [rojo](https://rojo.space) project. Based on the SM64 decompilation project hosted at: https://github.com/n64decomp/sm64

I wanted to make this public as a curiousity for anyone who wanted to know how I pulled it off. It **does not include any animations, sounds, or assets from Nintendo**. I do provide some of the scripts I used when I originally ported animations from the SM64 ROM into R15 Roblox avatars, but they are no longer in use.

Original game is hosted here:
https://www.roblox.com/games/10238956318/funny-64-bit-platformer

## Setting Up & Disclaimers
This project follows standard rojo conventions. You should install vscode and install the rojo extension, as well as the rojo plugin for Roblox Studio.

Any bugs or behavioral quirks of the physics in this are intentional and will not be fixed. **No help will be provided for anything in here. It is NOT intended to be used as a foundation for new games and you're on your own if you choose to do so.**

## Terms of Use

You *may* use this in your game, **but you must provide credit** to this repository and the SM64 Decompilation Project. I would **NOT** advise using it as a foundation for a platformer since it's very rigidly tied to the 30hz physics simulation code of Super Mario 64. It's weirdly programmed and not very intuitive to build off of.


Satchel

  [![Discord](https://discord.com/api/guilds/1162303282002272359/widget.png)](https://discord.gg/N2KEnHzrsW)

> [!WARNING]
> Satchel v1 is unmaintained to focus on v2 development. For a maintained alternative, see [Purse](https://purse.luau.page/).

Satchel is a modern open-source alternative to Roblox's default backpack. Satchel aims to be more customizable and easier to use than the default backpack while still having a "vanilla" feel. Installation of Satchel is as simple as dropping the module into your game and setting up a few properties if you like to customize it. It has a familiar feel and structure as to the default backpack for ease of use for both developers and players.

<https://github.com/ryanlua/satchel/assets/80087248/2cd3f164-6bf3-4c3b-a682-67a386f576d5>

## Documentation

See the [documentation site](https://satchel.luau.page) for more about Satchel. Find guides on how to get started, learn about the API, understand what Satchel is, and more.

If you see anything wrong, open a new [documentation issue](https://github.com/ryanlua/satchel/issues/new?template=documentation_issue.yml).

## Sponsors

Special thanks for our sponsors for supporting Satchel and it's future development. We distribute Satchel and provide updates for free, for anyone to use or modify.

      <source media="(prefers-color-scheme: dark)" srcset="docs/assets/sponsors/do-big-dark.png" height="60">
      <source media="(prefers-color-scheme: light)" srcset="docs/assets/sponsors/do-big-light.png" height="60">

    Do Big Studios

    5KFubi

[Become a sponsor](https://github.com/sponsors/ryanlua)

## Contributing

We welcome all contributions from the community. See the [contributing guidelines](.github/CONTRIBUTING.md) for details.

## License

Satchel is available under the Mozilla Public License 2.0 license. See [LICENSE.md](LICENSE.md) for details.


### Futuristic Luau for every universe.

Fusion is a portable Luau companion library for simpler, more descriptive code.

With Fusion, assemble straightforward chains of logic that are easy to understand,
predict and debug. Make strong guarantees about what your code will or won't do.
Build joyfully custom-fitted APIs to interact with the world outside of your code.

Fusion provides batteries-included configuration for Roblox, and fantastic extensibility
and integration for anything else Luau.

Piqued your interest? [Get going in minutes with our on-rails tutorial.](https://elttob.uk/Fusion/latest/tutorials)

## License

Fusion is licensed freely under MIT. Go do cool stuff with it, and if you feel
like it, give us a shoutout!


# Tutorials

Welcome to the Fusion tutorial section! Here, you'll learn how to build great things with Fusion, even if you're a complete newcomer to the library.

You'll not only learn how Fusion's features work, but you'll also be presented with wisdom from those who've worked with some of the largest Fusion codebases today.

But first, some advice from the maintainers...

** Fusion is pre-1.0 software. **

We _(the maintainers and contributors)_ work hard to keep releases bug-free and relatively complete, so it should be safe to use in production. Many people already do, and report fantastic results!

However, we mark Fusion as pre-1.0 because we are working on the design of the library itself. We strive for the best library design we can deliver, which means breaking changes are common and sweeping.

With Fusion, you should expect:

  * upgrades to be frictionful, requiring code to be rethought
  * features to be superseded or removed across versions
  * advice or best practices to change over time

You should _also_ expect:

  * careful consideration around breakage, even though we reserve the right to do it
  * clear communication ahead of any major changes
  * helpful advice to answer your questions and ease your porting process

We hope you enjoy using Fusion!

## What You Need To Know¶

These tutorials assume:

  * That you're comfortable with the Luau scripting language.
    * These tutorials aren't an introduction to Luau! If you'd like to learn, check out the [Roblox documentation](https://create.roblox.com/docs).
  * That - if you're using Roblox features - you're familiar with how Roblox works.
    * You don't have to be an expert! Knowing about basic instances, events and data types will be good enough.

Based on your existing knowledge, you may find some tutorials easier or harder. Don't be discouraged - Fusion's built to be easy to learn, but it may still take a bit of time to absorb some concepts. Learn at a pace which is right for you.

Back to top


# Rediscover the joy of coding.¶

Code is more dynamic, complex and intertwined than ever before. Errors cascade out of control, things update in the wrong order, and it's all connected by difficult, unreadable spaghetti.

No longer. Fusion introduces modern 'reactive' concepts for managing code, so you can spend more time getting your logic right, and less time implementing buggy boilerplate code connections.

Starting from simple roots, concepts neatly combine and build up with very little learning curve. At every stage, you can robustly guarantee what your code will do, and when you come back in six months, your code is easy to pick back up.

## Representing change¶

Fusion introduces ‘state objects’. They aren’t that complex, but allow you to write dynamic code that’s highly readable, behaves predictably and splits into parts easily.

State objects are used to represent changeable or dynamic values in your program. You can peek at their value at any time.

    -- For example, suppose this function returned a state object.
    local currentTimeObj = getCurrentTimeStateObject()

    -- State objects are objects...
    print(typeof(currentTimeObj)) --> table

    -- ...and you can peek at their value (or ‘state’) at any time.
    print(peek(currentTimeObj)) --> 0.0
    task.wait(5)
    print(peek(currentTimeObj)) --> 5.0

You can write out your logic using Fusion's built-in state objects. Here's the two basic ones, Value and Computed:

    -- Start tracking some new objects.
    local scope = Fusion:scoped()

    -- This creates a state object that you can set manually.
    -- You can change its value using myName:set().
    local myName = scope:Value("Daniel")

    -- This creates a state object from a calculation.
    -- It determines its own value automatically.
    local myGreeting = scope:Computed(function(use)
        return "Hello! My name is " .. use(myName)
    end)

    -- Discard all the objects.
    scope:doCleanup()

To watch what a state object does, you can use an Observer. For example, you can run some code when an object changes value.

    -- This observer watches for when the greeting changes.
    local myObserver = scope:Observer(myGreeting)

    -- Let’s print out the greeting when there’s a new one.
    local disconnect = myObserver:onChange(function()
        print(peek(myGreeting))
    end)

    -- This will run the code above!
    myName:set("Danny")

## Building instances¶

Fusion offers comprehensive APIs to build or enrich instances from code, so you can easily integrate with your game scripts.

Fusion provides dedicated functions to create instances. They allow you to easily configure your instance in one place.

    -- This will create a red part in the workspace.
    local myPart = scope:New "Part" {
        Parent = workspace,
        BrickColor = BrickColor.Red()

They offer powerful features to keep all your instance code close together. For example, you can listen for events or add children.

    -- This will create a rounded button.
    -- When you click it, it’ll greet you.
    local myButton = scope:New "TextButton" {
        Text = "Click me",

        [OnEvent "Activated"] = function()
            print("Hello! I’m a button.")
        end,

        [Children] = scope:New "UICorner" {
            CornerRadius = UDim.new(1, 0)

You can also plug state objects in directly. The instance updates as the state object changes value.

    -- Creating a state object you can control...
    local message = scope:Value("Hello!")

    -- Now you can plug that state object into the Text property.
    local myLabel = scope:New "TextLabel" {
        Text = message
    print(myLabel.Text) --> Hello!

    -- The Text property now responds to changes:
    message:set("Goodbye!")
    print(myLabel.Text) --> Goodbye!

## Animating anything¶

Fusion gives you best-in-class tools to animate anything you can think of, completely out of the box.

Fusion lets you use tweens or physically based springs to animate any value you want - not just instance properties.

    -- This could be anything you want, as long as it's a state object.
    local health = scope:Value(100)

    -- Easily make it tween between values...
    local style = TweenInfo.new(0.5, Enum.EasingStyle.Quad)
    local tweenHealth = scope:Tween(health, style)

    -- ...or use spring physics for extra responsiveness.
    local springHealth = scope:Spring(health, 30, 0.9)

Tween and Spring are state objects, just like anything else that changes in your program. That means it's easy to process them afterwards.

    -- You can round the animated health to whole numbers.
    local wholeHealth = scope:Computed(function(use)
        return math.round(use(health))
    end)

    -- You can format it as text and put it in some UI, too.
    local myText = scope:New "TextLabel" {
        Text = scope:Computed(function(use)
            return "Health: " .. use(wholeHealth)
        end)

You can even configure your animations using state objects, too. This makes it easy to swap out animations or disable them when needed.

    -- Define some tweening styles...
    local TWEEN_FAST = TweenInfo.new(0.5, Enum.EasingStyle.Elastic)
    local TWEEN_SLOW = TweenInfo.new(2, Enum.EasingStyle.Sine)

    -- Choose more dramatic styles at low health...
    local style = scope:Computed(function(use)
        return if use(health) < 20 then TWEEN_FAST else TWEEN_SLOW
    end)

    -- Plug it right into your animation!
    local tweenHealth = scope:Tween(health, style)

## Sparked your curiosity?¶

Those are the core features of Fusion, and they're the foundation of everything \- whether it’s complex 3D UI systems, procedural animation, or just a hello world app. It all fits on one page, and that's the magic. You don't have to keep relearning ever-more-complex tools as you scale up from prototype to product.

If you'd like to learn in depth, [we have a comprehensive beginner's tutorial track](https://elttob.uk/Fusion/0.3/tutorials), complete with diagrams, examples and code.

We would love to welcome you into our warm, vibrant community. Hopefully, we'll see you there :)

Back to top


# Error Safety

Code can fail unexpectedly for many reasons. While Fusion tries to prevent many errors by design, Fusion can't stop you from trying to access data that doesn't exist, or taking actions that don't make sense to the computer.

So, you need to be able to deal with errors that happen while your program is running.

## Fatality¶

An error can be either _fatal_ or _non-fatal_ :

  * fatal errors aren't handled by anything, so they crash your program
  * non-fatal errors are handled by Fusion and let your program continue

You're likely familiar with fatal errors. You can create them with `error()`:

Luau codeOutput

    print("before")
    print("before")
    print("before")

    error("Kaboom!")

    print("after")
    print("after")
    print("after")

    before
    before
    before
    Main:7: Kaboom!
    Stack Begin
    Main:7
    Fusion.State.Computed:74 function update
    Fusion.State.Computed:166 function Computed
    Main:6
    Stack End

You can make it non-fatal by protecting the call, with `pcall()`:

Luau codeOutput

    print("before")
    print("before")
    print("before")

    pcall(function()
        error("Kaboom!")
    end)

    print("after")
    print("after")
    print("after")

    before
    before
    before
    after
    after
    after

### Example¶

To demonstrate the difference, consider how Fusion handles errors in state objects.

State objects always run your code in a safe environment, to ensure that an error doesn't leave your state objects in a broken configuration.

This means you can broadly do whatever you like inside of them, and they won't cause a fatal error that stops your program from running.

Luau codeOutput

    print("before")
    print("before")
    print("before")

    scope:Computed(function()
        error("Kaboom!")
    end)

    print("after")
    print("after")
    print("after")

    before
    before
    before
    [Fusion] Error in callback: Kaboom!
      (ID: callbackError)
      ---- Stack trace ----
      Main:7
      Fusion.State.Computed:74 function update
      Fusion.State.Computed:166 function Computed
      Main:6

    Stack Begin
    Stack End
    after
    after
    after

These are _non-fatal_ errors. You don't _have_ to handle them, because Fusion will take all the necessary steps to ensure your program keeps running. In this case, the `Computed` object tries to roll back to the last value it had, if any.

    local number = scope:Value(1)
    local double = scope:Computed(function(use)
        local number = use(number)
        assert(number ~= 3, "I don't like the number 3")
        return number * 2
    end)

    print("number:", peek(number), "double:", peek(double))
        --> number: 1 double: 2

    number:set(2)
    print("number:", peek(number), "double:", peek(double))
        --> number: 2 double: 4

    number:set(3)
    print("number:", peek(number), "double:", peek(double))
        --> number: 3 double: 4

    number:set(4)
    print("number:", peek(number), "double:", peek(double))
        --> number: 4 double: 8

### Be Careful¶

Just because your program continues running, doesn't mean that it will behave the way you expect it to. In the above example, the roll back gave us a nonsense answer:

    --> number: 3 double: 4

This is why it's still important to practice good error safety. If you expect an error to occur, you should always handle the error explicitly, and define what should be done about it.

    local number = scope:Value(1)
    local double = scope:Computed(function(use)
        local number = use(number)
        local ok, result = pcall(function()
            assert(number ~= 3, "I don't like the number 3")
            return number * 2
        end)
        if ok then
            return result
        else
            return "failed: " .. err
        end
    end)

Now when the computation fails, it fails more helpfully:

    --> number: 3 double: failed: I don't like the number 3

As a general rule, your program should never error in a way that prints red text to the output.

## Safe Expressions¶

Functions like `pcall` and `xpcall` can be useful for catching errors. However, they can often make a lot of code clunkier, like the code above.

To help with this, Fusion introduces [safe expressions](https://elttob.uk/Fusion/0.3/api-reference/general/members/safe). They let you try and run a calculation, and fall back to another calculation if it fails.

    Safe {
        try = function()
            return -- a value that might error during calculation
        end,
        fallback = function(theError)
            return -- a fallback value if an error does occur
        end

To see how `Safe` improves the readability and conciseness of your code, consider this next snippet. You can write it using `Safe`, `xpcall` and `pcall` \- here's how each one looks:

pcallxpcallSafe

    local double = scope:Computed(function(use)
        local ok, result = pcall(function()
            local number = use(number)
            assert(number ~= 3, "I don't like the number 3")
            return number * 2
        end)
        if ok then
            return result
        else
            return "failed: " .. err
        end
    end)

    local double = scope:Computed(function(use)
        local _, result = xpcall(
            function()
                local number = use(number)
                assert(number ~= 3, "I don't like the number 3")
                return number * 2
            end,
            function(err)
                return "failed: " .. err
            end
        )
        return result
    end)

    local double = scope:Computed(function(use)
        return Safe {
            try = function()
                local number = use(number)
                assert(number ~= 3, "I don't like the number 3")
                return number * 2
            end,
            fallback = function(err)
                return "failed: " .. err
            end
    end)

`pcall` is the simplest way to safely handle errors. It's not entirely convenient because you have to check the `ok` boolean before you know whether the calculation was successful, which makes it difficult to use as part of a larger expression.

`xpcall` is an improvement over `pcall`, because it lets you define the fallback value as a second function, and uses its return value as the result of the calculation whenever an error occurs. However, it still returns the `ok` boolean, which has to be explicitly discarded.

`Safe` is an improvement over `xpcall`, because it does away with the `ok` boolean altogether, and _only_ returns the result. It also clearly labels the `try` and `fallback` functions so you can easily tell which one handles which case.

As a result of its design, `Safe` can be used widely throughout Fusion to catch fatal errors. For example, you can use it to conditionally render error components directly as part of a larger UI:

    [Children] = Safe {
        try = function()
            return scope:FormattedForumPost {
                -- ... properties ...
        end,
        fallback = function(err)
            return scope:ErrorPage {
                title = "An error occurred while showing this forum post",
                errorMessage = tostring(err)
        end

### Non-Fatal Errors¶

As before, note that _non-fatal_ errors aren't caught by `Safe`, because they do not cause the computation in `try()` to crash.

    -- The `Safe` is outside the `Computed`.
    -- It will not catch the error, because the `Computed` handles the error.
    local result = Safe {
        try = function()
            scope:Computed(function()
                error("Kaboom!")
            end)
            return "success"
        end,
        fallback = function(err)
            return "fail"
        end

    print(result) --> success

You must move the `Safe` closer to the source of the error, as discussed before.

    -- The `Safe` and the the `Computed` have swapped places.
    -- The error is now caught by the `Safe` instead of the `Computed`.
    local result = scope:Computed(function()
        return Safe {
            try = function()
                error("Kaboom!")
                return "success"
            end,
            fallback = function(err)
                return "fail"
            end
    end)

    print(peek(result)) --> fail

Back to top


#  AttributeChange -> [SpecialKey](https://elttob.uk/Fusion/0.3/api-reference/roblox/types/specialkey) ¶

    function Fusion.AttributeChange(
        attributeName: string
    ): SpecialKey

Given an attribute name, returns a [special key](https://elttob.uk/Fusion/0.3/api-reference/roblox/types/specialkey) which can listen to changes for attributes of that name.

When paired with a callback in a [property table](https://elttob.uk/Fusion/0.3/api-reference/roblox/types/propertytable), the special key connects the callback to the attribute's change event.

## Parameters¶

### attributeName  : string  ¶

The name of the attribute that the special key should target.

## Returns  -> [SpecialKey](https://elttob.uk/Fusion/0.3/api-reference/roblox/types/specialkey) ¶

A special key for listening to changes for attributes of that name.

Back to top


# Components

You can use functions to create self-contained, reusable blocks of code. In the world of UI, you may think of them as _components_ \- though they can be used for much more than just UI.

For example, consider this function, which generates a button based on some `props` the user passes in:

    type UsedAs<T> = Fusion.UsedAs<T>

    local function Button(
        scope: Fusion.Scope,
        props: {
            Position: UsedAs<UDim2>?,
            AnchorPoint: UsedAs<Vector2>?,
            Size: UsedAs<UDim2>?,
            LayoutOrder: UsedAs<number>?,
            ButtonText: UsedAs<string>
    )
        return scope:New "TextButton" {
            BackgroundColor3 = Color3.new(0, 0.25, 1),
            Position = props.Position,
            AnchorPoint = props.AnchorPoint,
            Size = props.Size,
            LayoutOrder = props.LayoutOrder,

            Text = props.ButtonText,
            TextSize = 28,
            TextColor3 = Color3.new(1, 1, 1),

            [Children] = UICorner { CornerRadius = UDim2.new(0, 8) }
    end

You can call this function later to generate as many buttons as you need.

    local helloBtn = Button(scope, {
        ButtonText = "Hello",
        Size = UDim2.fromOffset(200, 50)
    })

    helloBtn.Parent = Players.LocalPlayer.PlayerGui.ScreenGui

Since the `scope` is the first parameter, it can even be used with `scoped()` syntax.

    local scope = scoped(Fusion, {
        Button = Button
    })

    local helloBtn = scope:Button {
        ButtonText = "Hello",
        Size = UDim2.fromOffset(200, 50)

    helloBtn.Parent = Players.LocalPlayer.PlayerGui.ScreenGui

This is the primary way of writing components in Fusion. You create functions that accept `scope` and `props`, then return some content from them.

## Properties¶

If you don't say what `props` should contain, it might be hard to figure out how to use it.

You can specify your list of properties by adding a type to `props`, which gives you useful autocomplete and type checking.

    local function Cake(
        -- ... some stuff here ...
        props: {
            Size: Vector3,
            Colour: Color3,
            IsTasty: boolean
    )
        -- ... some other stuff here ...
    end

Note that the above code only accepts constant values, not state objects. If you want to accept _either_ a constant or a state object, you can use the `UsedAs` type.

    type UsedAs<T> = Fusion.UsedAs<T>

    local function Cake(
        -- ... some stuff here ...
        props: {
            Size: UsedAs<Vector3>,
            Colour: UsedAs<Color3>,
            IsTasty: UsedAs<boolean>
    )
        -- ... some other stuff here ...
    end

This is usually what you want, because it means the user can easily switch a property to dynamically change over time, while still writing properties normally when they don't change over time. You can mostly treat `UsedAs` properties like they're state objects, because functions like `peek()` and `use()` automatically choose the right behaviour for you.

You can use the rest of Luau's type checking features to do more complex things, like making certain properties optional, or restricting that values are valid for a given property. Go wild!

Be mindful of the angle brackets

Remember that, when working with `UsedAs`, you should be mindful of whether you're putting things inside the angled brackets, or outside of them. Putting some things inside of the angle brackets can change their meaning, compared to putting them outside of the angle brackets.

Consider these two type definitions carefully:

    -- A Vector3, or a state object storing Vector3, or nil.
    UsedAs<Vector3>?

    -- A Vector3?, or a state object storing Vector3?
    UsedAs<Vector3?>

The first type is best for _optional properties_ , where you provide a default value if it isn't specified by the user. If the user _does_ specify it, they're forced to always give a valid value for it.

The second type is best if the property understands `nil` as a valid value. This means the user can set it to `nil` at any time.

## Scopes¶

In addition to `props`, you should also ask for a `scope`. The `scope` parameter should come first, so that your users can use `scoped()` syntax to create it.

    -- barebones syntax
    local thing = Component(scope, {
        -- ... some properties here ...
    })

    -- scoped() syntax
    local thing = scope:Component {
        -- ... some properties here ...

It's a good idea to provide a type for `scope`. This lets you specify what methods you need the scope to have.

    scope: Fusion.Scope<YourMethodsHere>

If you don't know what methods to ask for, consider these two strategies.

  1. If you use common methods (like Fusion's constructors) then it's a safe assumption that the user will also have those methods. You can ask for a scope with those methods pre-defined.

         local function Component(
             scope: Fusion.Scope,
             props: {}
         )
             return scope:New "Thing" {
                 -- ... rest of code here ...
         end

  2. If you need more specific or niche things that the user likely won't have (for example, components you use internally), then you should not ask for those. Instead, create a new inner scope with the methods you need.

         local function Component(
             scope: Fusion.Scope,
             props: {}
         )
             local scope = scope:innerScope {
                 SpecialThing1 = require(script.SpecialThing1),
                 SpecialThing2 = require(script.SpecialThing2),

             return scope:SpecialThing1 {
                 -- ... rest of code here ...
         end

If you're not sure which strategy to pick, the second is always a safe fallback, because it assumes less about your users and helps hide implementation details.

## Modules¶

It's common to save different components inside of different files. There's a number of advantages to this:

  * it's easier to find the source code for a specific component
  * it keep each file shorter and simpler
  * it makes sure components are properly independent, and can't interfere
  * it encourages reusing components everywhere, not just in one file

Here's an example of how you could split up some components into modules:

Main filePopUpMessageButton

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15

    local Fusion = require(game:GetService("ReplicatedStorage").Fusion)
    local scoped, doCleanup = Fusion.scoped, Fusion.doCleanup

    local scope = scoped(Fusion, {
        PopUp = require(script.Parent.PopUp)
    })

    local ui = scope:New "ScreenGui" {
        -- ...some properties...

        [Children] = scope:PopUp {
            Message = "Hello, world!",
            DismissText = "Close"

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30

    local Fusion = require(game:GetService("ReplicatedStorage").Fusion)
    type UsedAs<T> = Fusion.UsedAs<T>

    local function PopUp(
        scope: Fusion.Scope,
        props: {
            Message: UsedAs<string>,
            DismissText: UsedAs<string>
    )
        local scope = scope:innerScope {
            Message = require(script.Parent.Message),
            Button = require(script.Parent.Button)

        return scope:New "Frame" {
            -- ...some properties...

            [Children] = {
                scope:Message {
                    Text = props.Message
                scope:Button {
                    Text = props.DismissText
    end

    return PopUp

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    local Fusion = require(game:GetService("ReplicatedStorage").Fusion)
    type UsedAs<T> = Fusion.UsedAs<T>

    local function Message(
        scope: Fusion.Scope,
        props: {
            Text: UsedAs<string>
    )
        return scope:New "TextLabel" {
            AutomaticSize = "XY",
            BackgroundTransparency = 1,

             -- ...some properties...

            Text = props.Text
    end

    return Message

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    local Fusion = require(game:GetService("ReplicatedStorage").Fusion)
    type UsedAs<T> = Fusion.UsedAs<T>

    local function Button(
        scope: Fusion.Scope,
        props: {
            Text: UsedAs<string>
    )
        return scope:New "TextButton" {
            BackgroundColor3 = Color3.new(0.25, 0.5, 1),
            AutoButtonColor = true,

             -- ...some properties...

            Text = props.Text
    end

    return Button

Back to top


#  Scope ¶

    export type Scope<Constructors = typeof(Fusion)> = {unknown} & Constructors

A table collecting all objects created as part of an independent unit of code, with optional `Constructors` as methods which can be called.

Scopes are not unique

Fusion can recycle old unused scopes. This helps make scopes more lightweight, but it also means they don't uniquely belong to any part of your program.

As a result, you shouldn't hold on to scopes after they've been cleaned up, and you shouldn't use them as unique identifiers anywhere.

## Learn More¶

  * [Scopes tutorial](https://elttob.uk/Fusion/0.3/tutorials/fundamentals/scopes)

Back to top


#  ScopedObject ¶

    export type ScopedObject = {
        scope: Scope<unknown>?,
        destroy: () -> ()

An object designed for use with [scopes](https://elttob.uk/Fusion/0.3/api-reference/memory/types/scope).

Objects satisfying this interface can be probed for information about their lifetime and how long they live relative to other objects satisfying this interface.

These objects are also recognised by [`doCleanup`](https://elttob.uk/Fusion/0.3/api-reference/memory/members/docleanup).

## Members¶

### scope  : [Scope](https://elttob.uk/Fusion/0.3/api-reference/memory/types/scope)<unknown>?  ¶

The scope which this object was constructed with, or `nil` if the object has been destroyed.

Unchanged until destruction

The `scope` is expected to be set once upon construction. It should not be assigned to again, except when the scope is destroyed - at which point it should be set to `nil` to indicate that it no longer exists inside of a scope. This is typically done inside of `oldestTask`.

### oldestTask  : unknown  ¶

The value inside of `scope` representing the point at which the scoped object will be destroyed.

Unchanged until destruction

The `oldestTask` is expected to be set once upon construction. It should not be assigned to again.

`oldestTask` is typically a callback that cleans up the object, but it's typed ambiguously here as it is only used as a reference for lifetime analysis, representing the point beyond which the object can be considered completely destroyed. It shouldn't be used for much else.

## Learn More¶

  * [Scopes tutorial](https://elttob.uk/Fusion/0.3/tutorials/fundamentals/scopes)

Back to top


# References

At some point, you might need to refer to another part of the UI. There are various techniques that can let you do this.

    local ui = scope:New "Folder" {
        [Children] = {
            scope:New "SelectionBox" {
                -- the box should adorn to the part, but how do you reference it?
                Adornee = ???,
            },
            scope:New "Part" {
                Name = "Selection Target",

## Constants¶

The first technique is simple - instead of creating the UI all at once, you can extract part of the UI that you want to reference later.

In practice, that means you'll move some of the creation code into a new `local` constant, so that you can refer to it later by name.

    -- the part is now constructed first, whereas before it was constructed second
    local selectionTarget = scope:New "Part" {
        Name = "Selection Target",

    local ui = scope:New "Folder" {
        [Children] = {
            scope:New "SelectionBox" {
                Adornee = selectionTarget
            },
            selectionTarget

While this is a simple and robust technique, it has some disadvantages:

  * By moving parts of your UI code into different local variables, your UI will be constructed in a different order based on which local variables come first
  * Refactoring code in this way can be bothersome and inelegant, disrupting the structure of the code
  * You can't have two pieces of UI refer to each other cyclically

Constants work well for trivial examples, but you should consider a more flexible technique if those disadvantages are relevant.

## Value Objects¶

Where it's impossible or inelegant to use named constants, you can use [value objects](https://elttob.uk/Fusion/0.3/tutorials/fundamentals/values) to easily set up references.

Because their `:set()` method returns the value that's passed in, you can use `:set()` to reference part of your code without disrupting its structure:

    -- `selectionTarget` will show as `nil` to all code trying to use it, until the
    -- `:set()` method is called later on.
    local selectionTarget: Fusion.Value<Part?> = scope:Value(nil)

    local ui = scope:New "Folder" {
        [Children] = {
            scope:New "SelectionBox" {
                Adornee = selectionTarget
            },
            selectionTarget:set(
                scope:New "Part" {
                    Name = "Selection Target",
            )

It's important to note that the value object will briefly be `nil` (or whichever default value you provide in the constructor). This is because it takes time to reach the `:set()` call, so any in-between code will see the `nil`.

In the above example, the `Adornee` is briefly set to `nil`, but because `selectionTarget` is a value object, it will change to the part instance when the `:set()` method is called.

While dealing with the brief `nil` value can be annoying, it is also useful, because this lets you refer to parts of your UI that haven't yet been created. In particular, this lets you create cyclic references.

    local aliceRef: Fusion.Value<Instance?> = scope:Value(nil)
    local bobRef: Fusion.Value<Instance?> = scope:Value(nil)

    -- These two `ObjectValue` instances will refer to each other once the code has
    -- finished running.
    local alice = aliceRef:set(
        scope:New "ObjectValue" {
            Value = bobRef
    )
    local bob = bobRef:set(
        scope:New "ObjectValue" {
            Value = aliceRef
    )

Value objects are generally easier to work with than named constants, so they're often used as the primary way of referencing UI, but feel free to mix both techniques based on what your code needs.

Back to top


#  Tween -> [Tween](https://elttob.uk/Fusion/0.3/api-reference/animation/types/tween)<T> ¶

    function Fusion.Tween<T>(
        scope: Scope<unknown>,
        goal: UsedAs<T>,
        tweenInfo: UsedAs<TweenInfo>?
    ) -> Tween<T>

Constructs and returns a new [tween state object](https://elttob.uk/Fusion/0.3/api-reference/animation/types/tween).

Use scoped() method syntax

This function is intended to be accessed as a method on a scope:

    local tween = scope:Tween(goal, info)

## Parameters¶

### scope  : [Scope](https://elttob.uk/Fusion/0.3/api-reference/memory/types/scope)<S> ¶

The [scope](https://elttob.uk/Fusion/0.3/api-reference/memory/types/scope) which should be used to store destruction tasks for this object.

### goal  : [UsedAs](https://elttob.uk/Fusion/0.3/api-reference/state/types/usedas)<T> ¶

The goal that this object should follow. For best results, the goal should be [animatable](https://elttob.uk/Fusion/0.3/api-reference/animation/types/animatable).

### info  : [UsedAs](https://elttob.uk/Fusion/0.3/api-reference/state/types/usedas)<TweenInfo>?  ¶

Determines the easing curve that the motion will follow.

## Returns  -> [Tween](https://elttob.uk/Fusion/0.3/api-reference/animation/types/tween)<T> ¶

A freshly constructed tween state object.

## Learn More¶

  * [Tweens tutorial](https://elttob.uk/Fusion/0.3/tutorials/animation/tweens)

Back to top


# Light & Dark Theme

This example demonstrates how to create dynamic theme colours using Fusion's state objects.

## Overview¶

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32

    local Fusion = --initialise Fusion here however you please!
    local scoped = Fusion.scoped

    local Theme = {}

    Theme.colours = {
        background = {
            light = Color3.fromHex("FFFFFF"),
            dark = Color3.fromHex("222222")
        },
        text = {
            light = Color3.fromHex("222222"),
            dark = Color3.fromHex("FFFFFF")

    -- Don't forget to pass this to `doCleanup` if you disable the script.
    local scope = scoped(Fusion)

    Theme.current = scope:Value("light")
    Theme.dynamic = {}
    for colour, variants in Theme.colours do
        Theme.dynamic[colour] = scope:Computed(function(use)
            return variants[use(Theme.current)]
        end)
    end

    Theme.current:set("light")
    print(peek(Theme.dynamic.background)) --> 255, 255, 255

    Theme.current:set("dark")
    print(peek(Theme.dynamic.background)) --> 34, 34, 34

## Explanation¶

To begin, this example defines a set of colours with light and dark variants.

    Theme.colours = {
        background = {
            light = Color3.fromHex("FFFFFF"),
            dark = Color3.fromHex("222222")
        },
        text = {
            light = Color3.fromHex("222222"),
            dark = Color3.fromHex("FFFFFF")

A `Value` object stores which variant is in use right now.

    Theme.current = scope:Value("light")

Finally, each colour is turned into a `Computed`, which dynamically pulls the desired variant from the list.

    Theme.dynamic = {}
    for colour, variants in Theme.colours do
        Theme.dynamic[colour] = scope:Computed(function(use)
            return variants[use(Theme.current)]
        end)
    end

This allows other code to easily access theme colours from `Theme.dynamic`.

    Theme.current:set("light")
    print(peek(Theme.dynamic.background)) --> 255, 255, 255

    Theme.current:set("dark")
    print(peek(Theme.dynamic.background)) --> 34, 34, 34

Back to top


# Observers

When you're working with state objects, it can be useful to detect various changes that happen to them.

Observers allow you to detect those changes. Create one with a state object to 'watch', then connect code to run using `:onChange()` or `:onBind()`.

    local observer = scope:Observer(health)
    local disconnect = observer:onChange(function()
        print("The new value is: ", peek(health))
    end)
    task.wait(5)
    disconnect()

## Usage¶

To create a new observer object, call `scope:Observer()` and give it a state object you want to detect changes on.

    6
    7
    8

    local scope = scoped(Fusion)
    local health = scope:Value(5)
    local observer = scope:Observer(health)

The observer will watch the state object for changes until it's destroyed. You can take advantage of this by connecting your own code using the observer's different methods.

The first method is `:onChange()`, which runs your code when the state object changes value.

Luau codeOutput

     8
     9
    10
    11
    12
    13
    14
    15
    16

    local observer = scope:Observer(health)

    print("...connecting...")
    observer:onChange(function()
        print("Observed a change to: ", peek(health))
    end)

    print("...setting health to 25...")
    health:set(25)

    ...connecting...
    ...setting health to 25...
    Observed a change to: 25

By default, the `:onChange()` connection is disconnected when the observer object is destroyed. However, if you want to disconnect it earlier, the `:onChange()` method returns an optional disconnect function. Calling it will disconnect that specific `:onChange()` handler early.

     8
     9
    10
    11
    12
    13
    14

    local disconnect = observer:onChange(function()
        print("The new value is: ", peek(health))
    end)

    -- disconnect the above handler after 5 seconds
    task.wait(5)
    disconnect()

The second method is `:onBind()`. It works identically to `:onChange()`, but it also runs your code right away, which can often be useful.

Luau codeOutput

     8
     9
    10
    11
    12
    13
    14
    15
    16

    local observer = scope:Observer(health)

    print("...connecting...")
    observer:onBind(function()
        print("Observed a change to: ", peek(health))
    end)

    print("...setting health to 25...")
    health:set(25)

    ...connecting...
    Observed a change to: 5
    ...setting health to 25...
    Observed a change to: 25

## What Counts As A Change?¶

If you set the `health` to the same value multiple times in a row, you might notice your observer only runs the first time.

Luau codeOutput

     8
     9
    10
    11
    12
    13
    14
    15
    16
    17

    local observer = scope:Observer(health)

    observer:onChange(function()
        print("Observed a change to: ", peek(health))
    end)

    print("...setting health to 25 three times...")
    health:set(25)
    health:set(25)
    health:set(25)

    ...setting health to 25 three times...
    Observed a change to: 25

This is because the `health` object sees that it isn't actually changing value, so it doesn't broadcast any updates. Therefore, our observer doesn't run.

This leads to improved performance because your code runs less often. Fusion applies these kinds of optimisations generously throughout your program.

Back to top


#  Child -> [Child](https://elttob.uk/Fusion/0.3/api-reference/roblox/types/child) ¶

    function Fusion.Child(
        child: Child
    ): Child

Returns the [child](https://elttob.uk/Fusion/0.3/api-reference/roblox/types/child) passed into it.

This function does no processing. It only serves as a hint to the Luau type system, constraining the type of the argument.

## Parameters¶

### child  : [Child](https://elttob.uk/Fusion/0.3/api-reference/roblox/types/child) ¶

The argument whose type should be constrained.

## Returns  -> [Child](https://elttob.uk/Fusion/0.3/api-reference/roblox/types/child) ¶

The argument with the newly cast static type.

## Learn More¶

  * [Parenting tutorial](https://elttob.uk/Fusion/0.3/tutorials/roblox/parenting)

Back to top


# Springs

Springs follow the value of other state objects using a physical spring simulation. This can be used for 'springy' effects, or for smoothing out movement naturally without abrupt changes in direction.

## Usage¶

To create a new spring object, call `scope:Spring()` and pass it a state object to move towards:

    local goal = scope:Value(0)
    local animated = scope:Spring(goal)

The spring will smoothly follow the 'goal' state object over time.

As with other state objects, you can `peek()` at its value at any time:

    print(peek(animated)) --> 0.26425...

To configure how the spring moves, you can provide a speed and damping ratio to use. Both are optional, and both can be state objects if desired:

    local goal = scope:Value(0)
    local speed = 25
    local damping = scope:Value(0.5)
    local animated = scope:Spring(goal, speed, damping)

You can also set the position and velocity of the spring at any time.

    animated:setPosition(5) -- teleport the spring to 5
    animated:setVelocity(2) -- from here, move 2 units/second

You can use many different kinds of values with springs, not just numbers. Vectors, CFrames, Color3s, UDim2s and other number-based types are supported; each number inside the type is animated individually.

    local goalPosition = scope:Value(UDim2.new(0.5, 0, 0, 0))
    local animated = scope:Spring(goalPosition, 25, 0.5)

## Damping Ratio¶

The damping ratio (a.k.a damping) of the spring changes the friction in the physics simulation. Lower values allow the spring to move freely and oscillate up and down, while higher values restrict movement.

### Zero damping¶

Zero damping means no friction is applied, so the spring will oscillate forever without losing energy. This is generally not useful.

### Underdamping¶

A damping between 0 and 1 means some friction is applied. The spring will still oscillate, but it will lose energy and eventually settle at the goal.

### Critical damping¶

A damping of exactly 1 means just enough friction is applied to stop the spring from oscillating. It reaches its goal as quickly as possible without going past.

This is also commonly known as critical damping.

### Overdamping¶

A damping above 1 applies excessive friction to the spring. The spring behaves like it's moving through honey, glue or some other viscous fluid.

Overdamping reduces the effect of velocity changes, and makes movement more rigid.

## Speed¶

The speed of the spring scales how much time it takes for the spring to move. Doubling the speed makes it move twice as fast; halving the speed makes it move twice as slow.

## Interruption¶

Springs do not share the same interruption problems as tweens. When the goal changes, springs are guaranteed to preserve both position and velocity, reducing jank:

This also means springs are suitable for following rapidly changing values:

Back to top


# Values

Now that you understand how Fusion works with objects, you can create Fusion's simplest object.

Values are objects which store single values. You can write to them with their `:set()` method, and read from them with the `peek()` function.

    local health = scope:Value(100)

    print(peek(health)) --> 100
    health:set(25)
    print(peek(health)) --> 25

## Usage¶

To create a new value object, call `scope:Value()` and give it a value you want to store.

    2
    3
    4
    5
    6

    local Fusion = require(ReplicatedStorage.Fusion)
    local doCleanup, scoped = Fusion.doCleanup, Fusion.scoped

    local scope = scoped(Fusion)
    local health = scope:Value(5)

Fusion provides a global `peek()` function. It will read the value of whatever you give it. You'll use `peek()` to read the value of lots of things; for now, it's useful for printing `health` back out.

    2
    3
    4
    5
    6
    7
    8

    local Fusion = require(ReplicatedStorage.Fusion)
    local doCleanup, scoped = Fusion.doCleanup, Fusion.scoped
    local peek = Fusion.peek

    local scope = scoped(Fusion)
    local health = scope:Value(5)
    print(peek(health)) --> 5

You can change the value using the `:set()` method. Unlike `peek()`, this is specific to value objects, so it's done on the object itself.

     6
     7
     8
     9
    10
    11

    local scope = scoped(Fusion)
    local health = scope:Value(5)
    print(peek(health)) --> 5

    health:set(25)
    print(peek(health)) --> 25

`:set()` returns the value you give it

You can use `:set()` in the middle of calculations:

    local myNumber = scope:Value(0)
    local computation = 10 + myNumber:set(2 + 2)
    print(computation) --> 14
    print(peek(myNumber)) --> 4

This is useful when building complex expressions. On a later page, you'll see one such use case.

Generally though, it's better to keep your expressions simple.

Value objects are Fusion's simplest 'state object'. State objects contain a single value - their _state_ , you might say - and that single value can be read out at any time using `peek()`.

Later on, you'll discover more advanced state objects that can calculate their value in more interesting ways.

Back to top


#  Tween ¶

    export type Tween<T> = StateObject<T>  & {
        kind: "Tween"

A specialised [state object](https://elttob.uk/Fusion/0.3/api-reference/animation/types/stateobject) for following a goal state smoothly over time, using a `TweenInfo` to shape the motion.

This type isn't generally useful outside of Fusion itself.

## Members¶

### kind  : "Tween"  ¶

A more specific type string which can be used for runtime type checking. This can be used to tell types of state object apart.

## Learn More¶

  * [Tweens tutorial](https://elttob.uk/Fusion/0.3/tutorials/animation/tweens)

Back to top