visualfx: Effects for a Scene

visualfx puts effects over a scene. Draw some shapes, then turn one on: a kaleidoscope, a glow, trails that spin away into a tunnel, an old television, a glitch. It is for any age and any level, and it is for enjoying: each effect is one line, and the graphics card does the work, so they are fast.

It is two libraries with the same names. visualfx.js is for pictures.js scenes and pyvisualfx is for Pyctures scenes. There are 94 effects and 16 ready-made looks, and Every effect has a picture of each one.

Where it works

  • In JavaScript there is nothing to import. Call any effect and the scene has all of them, in both spellings.
  • In Python a scene starts with from pyctures import * and from pyvisualfx import *.

In Circuitry both run in the preview beside your code. The effects are loaded only by a scene that uses one: a scene with none loads exactly what it always did. They need WebGL 2, which every current browser has, and they work on fewer pixels on a phone or a tablet to keep the scene quick.

Three things trip a beginner:

  • A kaleidoscope shows one slice of your picture. If the screen goes dark, nothing was drawn in that slice.
  • A backdrop is behind your drawing. Use background() with no colour, or you will paint over it.
  • Trails want a dark background.

Outside Circuitry, both libraries are open source, on GitHub: visualfx.js and pyvisualfx. Neither library is published yet, to npm or to PyPI. See On a page of your own and Using it with Python.

Turn one on

function draw() {
  background("black")
  circle(mouse.x, mouse.y, 40, "gold")
}

glow(1)                 // bright things shine
trails(1.5)             // and leave a trail that takes a second and a half to fade

That is a whole scene. There is nothing to call in draw: an effect that is on is applied to every frame, after your drawing.

Every effect is a function. Calling it turns it on. The first number is the thing you most often want to change, and it has a sensible value if you leave it out:

kaleidoscope(8)         // eight segments

A kaleidoscope shows one slice of your picture, the one that starts at the middle and points right, folded round and round. If the screen goes dark, nothing was drawn in that slice: fill the picture, or turn the slice with angle.

Change its settings

const pattern = kaleidoscope(8, { zoom: 1.5 })

function update(dt) {
  pattern.angle += 20 * dt        // turn it, twenty degrees a second
  pattern.x = mouse.x             // its middle follows the mouse
  pattern.y = mouse.y
}

An effect's settings are plain properties: read them, set them, add to them, every frame if you like. In JavaScript the settings after the first go in braces, and in Python they are given by name. A setting with a capital in JavaScript has an underscore in Python.

Lengths are in the same points as the rest of your scene and angles are in degrees, so a scene looks the same on a phone and on a big screen.

In JavaScript a setting can be given a function, and it is asked every frame:

const pattern = kaleidoscope(8)
let t = 0

function update(dt) {
  t += dt
}

pattern.segments = () => 4 + 2 * Math.floor(t)

Use several

kaleidoscope(6)
trails(0.7, { zoom: 1.5, spin: 25, hue: 90 })
glow(0.7)
vignette()

Effects are applied in the order you turn them on. Here the picture is folded, then the folded picture leaves trails, then all of it glows. Swap two lines and you get a different picture.

Each effect has off(), on() and remove(), and amount from 0 to 1 for how much of it is mixed in. clearEffects() takes them all away (clear_effects() in Python).

const shine = glow(1)
shine.amount = 0.5      // half of it
shine.off()             // none of it, and it costs nothing
shine.on()
clearEffects()

Or take a ready-made look

look("arcade")          // the curved glass of an old arcade machine

A look is several effects at once: dream, arcade, vhs, neon, comic, underwater, oldfilm, disco, terminal and more. It has amount, off(), on() and remove() for the lot:

const glass = look("arcade")
glass.amount = 0.5

In JavaScript a look also gives its parts back by name:

const tape = look("vhs")
tape.glitch.strength = 1

Looks and palettes has a picture of each look and what it is made of.

A picture with no drawing at all

metaballs("lava")       // blobs that melt into each other
glow(0.6)

function draw() {
  background()          // with no colour: wipe the picture clear
}

plasma, starfield, metaballs, flow and eleven more are backdrops: pictures the graphics card makes by itself. They are behind whatever you draw, and show wherever you have left the canvas clear. So do not paint a background colour over them: background() with no colour wipes the canvas clear each frame. Backdrops has them all.

Many of them take a palette, which is a name ("rainbow", "fire", "ocean", "sunset", "neon", "candy" and more) or your own list of colours:

plasma(["navy", "magenta", "gold"])

Trails

Trails are how the classic screensaver tunnel is made. Each frame is last frame's picture, a little bigger (zoom: 2 doubles its size every second), a little turned (spin, degrees a second) and a little changed in colour (hue), with this frame's drawing on top.

const tail = trails(2.5, { zoom: 1.9, spin: 50, hue: 140 })
let t = 0

function update(dt) {
  t += dt
}

function draw() {
  background("black")
  for (let i = 0; i < 5; i++) {
    const a = t * 1.3 + i * 1.2566
    star(width / 2 + 50 * Math.cos(a), height / 2 + 50 * Math.sin(a), 11, 5, "gold")
  }
}

Trails are measured in seconds, not frames, so a scene looks the same at 30 and at 120 frames a second. They want a dark background: only what is brighter than the trail is seen over it.

A dial that turns an effect

An effect is something dials can turn. The dial takes its range from the effect:

const soft = blur(4)
dial("blur", soft, "radius")

The dials are drawn over the effects, so they are never blurred or folded.

Mistakes

A mistake is said in plain words, with what there is instead:

kaleidoscope has no setting called "segmnts". Its settings are segments, angle, zoom, x, y, amount
mirror's side is one of "left", "right", "top", "bottom", "four", not "sideways"
There is no look called "arcad". There are dream, arcade, vhs, neon, comic, underwater, oldfilm, disco, …

A device with no graphics card the browser can use (no WebGL 2) shows the scene without the effects, and says so once in the console. Nothing else stops.

effectNames(), lookNames() and paletteNames() list what there is to a program (effect_names(), look_names(), palette_names()).

Not here yet

Transitions between two scenes, slit-scan (rows from older frames), and effects on one sprite alone are not in the library.

What's in this section