# Signals

Signals represent a value that may change over time. They can be used to define dependencies between the state of the animation. This way, when a value changes, all other values that depend on it get automatically updated.

## Overview​

Signals for primitive types are created using the `createSignal()` function, where the first argument specifies their initial value:

``import {createSignal} from '@motion-canvas/core';const signal = createSignal(0);``

Additionally, each complex type has a static `createSignal()` method that can be used to create a signal for said type:

``import {Vector2} from '@motion-canvas/core';const signal = Vector2.createSignal(Vector2.up);``

Properties of every node are also represented by signals:

``const circle = <Circle />;const signal = circle.fill;``

Once created, signals can be invoked to perform one of the three possible actions (The action is chosen based on the number of arguments):

1. retrieve the value:
``const value = signal();``
2. update the value:
``signal(3);``
3. create a tween for the value:
``yield * signal(2, 0.3);``

Instead of the actual value, a signal can be provided with a function that computes the value dynamically. Consider the following example:

``const radius = createSignal(1);const area = createSignal(() => Math.PI * radius() * radius());console.log(area()); // 3.141592653589793radius(2);console.log(area()); // 12.566370614359172``

Here, the `area` signal uses the `radius` signal to compute its value.

## Explanation​

To better understand how signals work, let's modify the example from before to see when exactly the area is calculated:

``const radius = createSignal(1);const area = createSignal(() => {  console.log('area recalculated!');  return Math.PI * radius() * radius();});area(); // area recalculated!area();radius(2);area(); // area recalculated!radius(3);radius(4);area(); // area recalculated!``

This demonstrates three important aspects of signals:

### Laziness​

Signals are only calculated when their value is requested. The first `"area recalculated!"` message is logged to console only after `area()` is called.

### Caching​

Once the signal is calculated, its value is saved and then returned during subsequent calls to `area()`. That's why nothing is logged to the console during the second call. This aspect of signals makes them perfect for caching computationally heavy operations. In fact, Motion Canvas uses signals internally to cache things such as matrices.

### Dependency tracking​

The `area` signal keeps track of other signals it depends on. When we change the `radius` signal, the `area` signal is notified about that. But it doesn't get recalculated immediately - laziness is still at play. We can modify the radius however many times we want, but the `area` will be recalculated only once its value is requested again by calling `area()`.

## `DEFAULT` values​

Signals keep track of the initial values specified during creation. At any time, we can reset a signal to its initial value by passing the `DEFAULT` symbol to it:

``import {DEFAULT, createSignal} from '@motion-canvas/core';const signal = createSignal(3); // <- initial value is 3signal(2);signal(); // <- value is now 2signal(DEFAULT);signal(); // <- value is reset back to 3``

We can also use the `DEFAULT` symbol for tweening:

``yield * signal(DEFAULT, 2);``

Resetting to the default value is especially useful with node properties. In the example below, we set the `lineHeight` of the `Txt` node to `150%`. This will override its default value, which would be simply inherited from its parent:

``const text = createRef<Txt>();view.add(  <Txt lineHeight={'150%'} ref={text}>    Hello world!  </Txt>,);``

If we want to reset the `lineHeight` back to the default, inherited value, we can do so with `DEFAULT`:

``text().lineHeight(DEFAULT);``

## Complex example​

We can use the fact that properties of nodes are represented by signals to construct scenes that automatically update when the data changes. Following the previous example, let's create a visualisation for the area of the circle:

Below you'll find the code used to create this animation. We highlighted all the places where signals are used:

``import {makeScene2D} from '@motion-canvas/2d/lib/scenes';import {Circle, Txt, Line} from '@motion-canvas/2d/lib/components';import {createSignal} from '@motion-canvas/core/lib/signals';import {Vector2} from '@motion-canvas/core/lib/types';import {waitFor} from '@motion-canvas/core/lib/flow';export default makeScene2D(function* (view) {  const radius = createSignal(3);  const area = createSignal(() => Math.PI * radius() * radius());  const scale = 100;  const textStyle = {    fontWeight: 700,    fontSize: 56,    offsetY: -1,    padding: 20,    cache: true,  };  view.add(    <>      <Circle        width={() => radius() * scale * 2}        height={() => radius() * scale * 2}        fill={'#e13238'}      />      <Line        points={[          Vector2.zero,          () => Vector2.right.scale(radius() * scale),        ]}        lineDash={[20, 20]}        startArrow        endArrow        endOffset={8}        lineWidth={8}        stroke={'#242424'}      />      <Txt        text={() => `r = \${radius().toFixed(2)}`}        x={() => (radius() * scale) / 2}        fill={'#242424'}        {...textStyle}      />      <Txt        text={() => `A = \${area().toFixed(2)}`}        y={() => radius() * scale}        fill={'#e13238'}        {...textStyle}      />    </>,  );  yield* radius(4, 2).to(3, 2);  yield* waitFor(1);});``

With this setup, all we need to do is animate the `radius` signal, and the rest of the scene will adjust accordingly:

``yield * radius(4, 2).to(3, 2);``