Stages

Stages

At the beginning of the year, I had a chat with 12 min albums record label. Dante, the guy from the label, said that if I record mini album (12 minutes) they would be glad to publish it. One of the requirements from their side was that, together with audio, I needed to provide video. So, naturally, I spent the last few months developing a tool to make trippy animations. Like this one:

TLDR:

The spacebar hides/shows controls. Click on animation makes it fullscreen.

While animation does work on mobile, controls are too big to be comfortable on mobile.

Open the link and drag some sliders around.

#Documentation.

Top left corner has 0,0 coordinates. X axis goes from left to right, Y axis goes from top to bottom.

There is two types of controls and factory to spawn them.

  1. Value producers. Some of them are just values like zero or one. Some of them need other vales to produce values. For example, math needs two other values to produce a value.
  2. Outputs, are the ones that draw something on the screen. A line is an example of an output.

All changes are synced to the URL. That has two side effects:

  1. Browser back/forth works like undo/redo
  2. If you come up with a nice animation, you can embed it into anywhere just by URL.

It does crush on division by zero or if connections form a loop. Browser back (undo) does fix the issue most of the time.

#Built in value producers.

  • zero: 0
  • one: 1
  • two: 2
  • width: width of the browser window
  • height: height of the browser window
  • i - In each frame, some amount of shapes will be drawn, this will be the serial number of the shape drawn.
  • now - Time since animation start in milliseconds.

#Factory

The factory spawns any available control.

#Slider

Produces a value that can be changed by user in UI. Minimum slider resolution is 0.00001, if min and max are close enough to each other.

#Math

Produces a value that is a result of math operations.

  • sum - adds two values
  • sub - subtracts two values
  • mul - multiplies two values
  • div - divides two values
  • avg - averages two values

In order to use screen real estate efficiently, math has two blocks a (top) and b (bottom). Each block has lhs and rhs values and mode that specifies operation. Those two blocks produce two values with _a and _b postfixes.

#Oscillator

Produces a value that oscillates between min and max in time specified by raise and fall.

All oscillators are synced to the same zero time.

raise and fall are in milliseconds.

#Random

Produces a random value between min and max

#Line output

Connects vertices amount of vertices by a straight line.

For each frame, it will read values from vertices and sr (sample rate) parameters. Then it will read values from x and y, vertices amount of time, advancing now by sr and i by one.

#Logic

Produces a value that is a result of logic operation.

  • eq: left hand equals right hand
  • neq: left hand not equals right hand
  • gt: left hand greater than right hand
  • lt: left hand less than right hand
  • gte: left hand greater than or equals right hand
  • lte: left hand less than or equals right hand

If logic operation evaluates to true, value from is_true connector will be returned, otherwise value from is_false.

Writing documentation is hard.

If you know how to make it better, please let me know.

#Walkthrough A

In this part we will learn how to draw lines and position them on the screen.

#Simple line

Lets open empty stage, add a slider with name y and line with name line to the stage. Nothing happens, that because we do not have any connections yet. Lets connect:

  • line.x to width
  • line.y to y
  • line.vertices to height
  • line.sr to one

Now we have a line that goes from the left side of the screen to the right side of the screen. When you move the y slider, the line will move up and down.

simple line

#Vertical line that always in the center of the screen

In a previous example we were able to draw a line at position that can be adjusted manually. If we want to draw a line at specific position let's, say at center of the screen, we need math. With math we will divide height value by two.

Lets open empty stage, add a math with name center and line with name vl to the stage. Then add connections:

  • center.mode_a to div
  • center.lhs_a to height
  • center.rhs_a to two
  • vl.x to width
  • vl.y to center_a
  • vl.vertices to width
  • vl.sr to one

vertical line in center

#Horizontal line that always in the center of the screen

Let's add another line that will be drawn at the center of the screen. Use previous stage, add one more line control with name hl and connect it like this:

  • center.mode_b to div
  • center.lhs_b to width
  • center.rhs_b to two
  • hl.x to center_b
  • hl.y to i
  • hl.vertices to height
  • hl.sr to one

horizontal line in center

#Triangle wave in the top right corner

With oscillator control we can create a triangle wave. And to make it more interesting lets draw it in the top left corner.

To achieve that we need to add three controls:

  • slider with name y_t to control the frequency
  • oscillator with name y to produce y value
  • line with name tr to draw in top left corner

And connect them like this:

  • y.min to zero
  • y.max to center_a
  • y.raise to y_t
  • y.fall to y_t
  • tl.x to i
  • tl.y to y
  • tl.vertices to center_a
  • tl.sr to one

top left corner

#Triangle wave in the bottom right corner

Let's add another triangle wave, but this time in the bottom right corner. To make it more interesting, we will make it run in a different direction. To achieve that we need a bit more math, and of course, another line control.

Add following controls:

  • math with name br_pos to calculate x value
  • line with name br to draw in bottom right corner

Then connect them like this:

  • br_pos.mode_a to sub
  • br_pos.lhs_a to width
  • br_pos.rhs_a to i
  • br.x to br_pos_a
  • br.y to br_pos_b
  • br.vertices to center_a
  • br.sr to one

bottom right corner

#Movement in both directions

We have two corners left. Let's fill top right corner with graph that moves in both directions. To achieve that we need to add following controls:

  • slider with name x_t to control frequency
  • oscillator with name xto produce x value
  • line with name tr to draw in top right corner

And connect them like this:

  • x.min to center_a
  • x.max to width
  • x.raise to x_t
  • x.fall to zero
  • tr.x to x
  • tr.y to y
  • tr.vertices to width
  • tr.sr to one

top right corner

#Distortion

Last corner to fill! You know what? We will get graph from previous step and distort it. To do that we will need a bit more stuff than usual. Lets add following controls:

  • slider with name n_amoint to control amount of distortion
  • math with name n_width to control width of the noise
  • random with name noise to generate noise
  • math with name bl_pos to calculate position
  • math with name bl_dis to calculate distortion
  • line with name bl to draw distorted graph

And connect them like this:

  • n_width.mode_a to sub
  • n_width.lhs_a to zero
  • n_width.rhs_a to n_amount
  • noise.min to n_width_a
  • noise.max to n_amount
  • bl_pos.mode_a to sub
  • bl_pos.lhs_a to x
  • bl_pos.rhs_a to center_a
  • bl_pos.mode_b to sum
  • bl_pos.lhs_b to y
  • bl_pos.rhs_b to center_b
  • bl_dis.mode_a to sum
  • bl_dis.lhs_a to bl_pos_a
  • bl_dis.rhs_a to noise
  • bl_dis.mode_b to sum
  • bl_dis.lhs_b to bl_pos_b
  • bl_dis.rhs_b to noise
  • bl.x to bl_dis_a
  • bl.y to bl_dis_b
  • bl.vertices to width
  • bl.sr to one

bottom right corner

Congrats!

You just finished first ever Stages tutorial!

How was it?

#Walktrough B

In this part we will recreate Stages logo.

#U shape

Lets start with a U shaped wave.

Open an empty stage and add following controls:

  • slider with name y_t to control frequency of first oscillators
  • oscillator with name y_min_mod to modulate min value
  • oscillator with name y to get y coordinate
  • line with name line to draw a line

Then connect them like this:

  • y_min_mod.min to zero
  • y_min_mod.max to height
  • y_min_mod.raise to y_t
  • y_max_mod.fall to y_t
  • y.min to y_min_mod
  • y.max to height
  • y.raise to y_t
  • y.fall to y_t
  • line.x to i
  • line.y to y
  • line.vertices to width
  • line.sr to one

U shape

Logo

To achieve the same result as on the logo we need one more oscillator control that runs with a higher frequency. Lets add to previous stage following controls:

  • oscillator with name y_min_mm to modulate y_min_mod.min
  • slider with name mm_t to control frequency of modulation

And connect them like that:

  • y_min_mm.min to zero
  • y_min_mm.max to height
  • y_min_mm.raise to mm_t
  • y_min_mm.fall to mm_t
  • y_min_mod.min to y_min_mm

Stages logo

Yay!

The logo is complete, and no AI was harmed in the process.

What do you think?

#Walktrough C

The idea was to introduce logic control, but I failed to find the time. Here is the final result:

#Pulsed distortion

pulsed distortion

This post is too long anyway.

I will stop here.

If you have any questions or suggestions, feel free to open an issue on the Stages repo.

#TODO

Plans for the future looks like that:

  • Resolve circular dependencies.
  • Resolve division by zero.
  • Color control. Most likely in HSB format.
  • More shapes. Circles, squares, etc.
  • Trigonometry. cos, sin, tan, etc.
  • Math control requires more options: powers, roots, logs, etc.
  • Actual audio support.
  • Gates and ADSR.
  • MIDI support would be awesome.
  • Better docs.
  • Better UI.
(◕‿◕)

I am extremely curious what you will create with Stages. Send me links!

I published my first npm package: filters

I published my first npm package: filters

@barhamon/filters - generic way to manipulate data filtering options and pass them around

Redux is not bad

Redux is not bad

This is why you should use redux as a state manager for your app

How to iterate over the array in JavaScript and the root of all evil

How to iterate over the array in JavaScript and the root of all evil

benchmarking array methods vs for loop vs iterators vs generators