An effect is smoke from a chimney, a burner's flame, a spell's crack (to be built), rain round the eye: a cloud of small glowing or soft things drawn by the graphics card. It is described, not recorded: a recipe of numbers that each machine turns into particles. An effect is a part with no shape, placed and carried like any part; what differs is its aspect, and how it is switched: always on, switched by state, or started by an event. Effects are seen only. None changes anything another engine reads, so none is ever sent between machines beyond the moment it starts.

Words used on this page:

Particle
One small drawn thing (a puff, a spark, a drop) with a place, a size, a color and an age.
Emitter
A description of how particles are born and how they move: a rate, a lifetime, a launch shape, speeds, forces, sizes and colors over a life.
Event
One firing of an emitter: a seed, a start and a stop on the scene's clock, and a placement.
Additive
Drawn by adding light to what is behind (flames, sparks); smoke is drawn over what is behind instead.

How you write it

Always on

part chimney
  part smoke: vfx.smoke(rate: rate(6, per: sim(1 s)), life: sim(8 s), speed: 0.9 m/s,
                        size: 0.26 m ..= 1.5 m, push: rate(0.05 m/s, per: sim(1 s)) * wind.direction,
                        seed: 5391, start: sim(0 s), stop: never),
              at: pot.mouth

A slow, soft stream of wood smoke: six puffs a second, each living about 8 s, leaving at about 0.9 m/s and slowing as it cools, 0.26 m across at the pot and 1.5 m by the end, fading in quickly and out over the last two-thirds of its life, leaning downwind to about 0.5 m/s. stop: never is a stop that never comes. vfx.smoke is a spelling: the vfx package has a smoke drawing style but no named presets; the numbers are the caller's (below).

Switched by state

part balloon
  field launched: moment = never, mutable: true, lives: server
  part envelope: ...
    part burner: ...
      part flame: effect(vfx.layers([core, tongue, embers]), seed: seed),
                  at: nozzle
      part pilot: effect(vfx.layers([pilot]), seed: seed, start: sim(0 s), stop: never),
                  at: nozzle + (5 cm, 1 cm, 0)
  fn burst(t: moment) span { ... }                         // the burner's burst in force at t
  derive(burner.flame.firing, burst(now(sim)))

The burner's flame is three layers of particles, a short bright blue-white core, a long orange tongue and embers, each with its own rate, lifetime, speed and color over its life, the tongue and embers with buoyancy and a wobble. When it burns is a subscription over the clock and the one stored moment the ride began: 3 s on in every 4 on the ground, 3.2 s in every 5 climbing, 1.5 s in every 20 after the top, none in the last 45 s. Each burst gets its own seed, a hash of the moment it starts and the balloon's seed, so no two bursts look alike and every machine draws the same burst. The pilot light is a part of its own so a burst can end without putting it out. effect(vfx.layers([...])) is a spelling of a list of emitter layers, not a vfx function.

Started by an event

on(zapLanded(on: b), {                                   // illustration
  spawn(crack(seed: hash(now(sim))), under: b, at: b.chest, life: sim(0.4 s));   // the crack is (to be built)
})

The crack is to be built. The island's zap has the cry only (Sounds), a counter and the pose; it draws no crack. The message zapLanded is raised and handled as Events describes.

What it means

A description, never a simulation

An emitter is a description, never a simulation: particle i of an event is born i / rate seconds after the event starts, and where it is at any age is a closed-form function of (event seed, layer seed, i, age). The forces (drag, gravity and wind, buoyancy, cooling) are solved in closed form. So the renderer draws each effect as it is at this moment, a paused clock holds it still, a clock run forward finds it where it would be, and every screen shows the same smoke with nothing kept or sent. A particle's place on the graphics card is close to the main processor's, not identical: an effect is cosmetic by nature.

Three ways to switch, one kind of thing

Every effect has a start and a stop on the scene's clock; stop is when spawning stops (never, or a time): particles already born live out their lives. The always-on smoke, the switched burner and an effect started by an event differ only in how they get those two values. A switch costs one start and one stop per burst on the network; between them, nothing.

Where it runs

Effects are drawn in each view; a server has none, and the names mean nothing there. An effect drawn into a mirror's or portal's picture is drawn again for that camera.

Effects reading the world

An always-on effect can still read a field: the smoke leans on the wind's direction, wind.direction, and states its own push (0.05 m/s each second, which its drag settles to 0.5 m/s); each ground effect does the same (Sky and weather: the wind). Written over the wind's speed, the same line would stream the smoke flat in a storm. The weather's rain and snow are drawn by falling, whose options are listed with the weather (Sky and weather). Whether an effect's numbers may change once it has started is open: vfx's emitter is fixed for an event's life.

The fire as a shape

A brazier that burns all the time need not use an effect: its flame can be a still shape, an outline turned round an axis like a tall teardrop, in a glowing orange material that casts no shadow, with a steady light over it. The content should say "fire", and the browser may draw it as particles or as a glowing shape: that is a quality choice made below. Whether a light should flicker with its flame is open.

The packages

vfx: stateless emitters

Visual effects drawn by the graphics card, beginning with particles.

PieceWhat it holds
Emittera layer seed, rate, lifetime and its jitter; launch shape (point, disc, sphere), radius, spread (a cone about local up), speed and its jitter; drag, gravity (and wind), buoyancy, cooling, wobble; size at start and end, stretch, fade in and out; four color keys (linear, high range; alpha is intensity) and when each applies; a trail time
Eventseed, start, stop, placement; Event.at(seed, start, placement) never stops
Stylessprites (glowing, stretched by motion), trails (ribbons along each particle's path), smoke (soft round puffs drawn over what is behind them)
ParticleRendereraddEmitter(style), update(handle, emitter, event, now), render(...)

Blending is unsorted. Not built: horizontal-only wobble, launch always about local up, removing emitters, textures or flipbooks.

effectsfx: effects as data, simulated

Effects at the standard of the best shooters, defined as data and drawn by the graphics card. It sits beside vfx, which keeps its stateless emitters. An Effect is a record of layers:

It is registered once and played by EffectPlayer.play(id, origin, axis, velocity, seed, now), at most 12 particle layers. Because collision depends on each viewer's frame, effects with collision are free to differ: only the event is shareable. Not done: a text form of an effect, sorting, sound hooks, a fluid that moves.

How it connects

Open questions

  1. Live effect parameters: may an effect's numbers change once it has started?
  2. How identical effects must be across machines: stateless and close, or free to differ where they collide with what each viewer draws.
  3. Code or a data record: vfx's emitters are code; effectsfx's effects are data with no text form yet.
  4. A light bound to an effect, and a heat haze over a flame.
  5. Fire as a word: whether content names "fire" and the browser chooses particles or a glowing shape.
  6. A lifetime on a spawned effect.

← Sky and weather | Contents | Sounds →