NatureGL Magicv1.0.0

Guides

Genie smoke and vortex rings

Both effects are smoke that has to hold its shape. The genie lamp is a warm, glowing jet wound into a slow column that puffs a ring every few seconds. The vortex-ring cannon fires tight, rolling rings that travel across the whole box.

#Genie smoke

genieLamp: blue smoke from the spout, wound into a column by a swirl field 1.84 m above it.
js
const lamp = magic.addEffect('genieLamp', {
  position: [0.56, 1.06, 0],   // the spout tip
  velocity: [0.7, 1.5, 0],     // jet velocity; retarget() keeps its magnitude
  color: '#4d8cff',
});
lamp.puff();                   // launch a smoke ring now

position is the spout tip, and every other offset is relative to it, so move() carries the whole column with it. The effect adds:

  • a jet emitter with density 22, glow 6 and temperature 0.55, driven toward velocity with blend 0.5. The heat makes it rise.
  • a swirl column centred at swirl.offset [-0.11, 1.84, 0] above the spout, around a vertical axis. It spins at 1.4, pulls in at 0.3 and lifts at 0.5, over a 1.05 m radius and 1.5 m thickness.
  • a ring every rings.interval (3.6 s), launched rings.offset above the spout along rings.axis [0.15, 1, 0]. A genie ring lays a heavy puff of dye in its first 60 ms (early: 140) and none after that (dyeRate: 0). It has no analytic torus or confinement boost, so it's softer and shorter-lived (2.6 s) than a cannon ring. At most two are in flight.
  • occasional sparks near the spout (sparks.rate 0.05).
OptionDefault
position, velocity[0.56, 1.06, 0], [0.7, 1.5, 0]
color, radius[0.3, 0.6, 1], 0.2
glow, density, temperature, blend6, 22, 0.55, 0.5
swirl{ offset: [-0.11, 1.84, 0], axis: [0, 1, 0], radius: 1.05, speed: 1.4, pull: 0.3, thickness: 1.5, lift: 0.5 }
rings{ interval: 3.6, offset: [0.05, 0.38, 0], axis: [0.15, 1, 0], speed: 1.1, drag: 0.35, life: 2.6, radius: 0.32, core: 0.12, shell: 0.07, dyeRate: 0, early: 140, glow: 1.2, confinement: 0, torus: null }
sparks{ rate: 0.05, offset: [0, 0.15, 0], radius: 0.18, life: 0.45, jitter: 1.5, colorA, colorB }

Set rings: { interval: 0 } to puff only when you call puff(), and swirl: { speed: 0 } for a free plume with no column.

#Vortex rings

vortexRings: the cannon fires every 1.05 s. Each ring lives 4.5 s, slows from 2.1 m/s with drag: 0.42, and fades as it nears the open faces of the box.
js
const cannon = magic.addEffect('vortexRing', {
  position: [-0.672, 0.948, -0.495],   // launch point, just in front of the muzzle
  direction: [0.781, 0.28, 0.558],
  interval: 1.05,                      // 0 = only when you call fire()
});
cannon.fire();                         // a ring now
cannon.fire({ radius: 0.6, color: '#ff4f8e' });   // one-off overrides for this shot
cannon.rings;                          // TravellingRing[] in flight

A ring isn't a mesh. Each frame, the solver steers the fluid toward a velocity field that circulates around the ring's core and translates along its axis, and the ring lays dye in a thin shell around that core. Three things keep it a ring instead of a blob:

  1. A local confinement boost. confinement (4) is added to the vorticity-confinement gain around the core, so the grid keeps the core tight on its own.
  2. An analytic torus. torus draws a thin rolling torus in the volume on top of the fluid dye, so the hole never blurs shut at coarse grids. sharpness sets the profile (2 = gaussian, higher = crisper edge), and structure sets how much ring-local rolling noise it has.
  3. A fade at the domain faces. As the ring's rim comes within 0.45 m of an open side or the top, the ring's dye, glow and torus fade out, so it never clips into a half-donut.

Over its life the ring slows as speed · e^(−drag · age), grows by up to growth (18 %), wobbles by wobble, and its dye and glow fade. For the first 60 ms it lays early (6) dye per second and triple glow, which gives the launch puff.

OptionDefault
position, directionsee aboveLaunch point and travel direction
color, glow[1, 0.86, 0.72], 0.16
interval1.05Seconds between automatic shots (0 = manual)
speed, drag, life2.1, 0.42, 4.5Launch speed (m/s), slow-down (1/s), lifetime (s)
radius, core, shell0.44, 0.17, 0.055Ring radius, velocity-core radius, dye-shell thickness (m)
growth, wobble0.18, 1Radius growth over life; axis wobble amount
dyeRate, early0.6, 6Dye per second while travelling, and in the first 60 ms
confinement4Extra vorticity-confinement gain around the core
torus{ core: 0.085, density: 3, spread: 0.35, fadePower: 0.6, sharpness: 2.6, structure: 0.75, roll: 0.5 }density: 0 turns the overlay off
maxRings4The oldest ring is dropped past this

The 4 ring slots are shared with the genie lamp's rings. See per-frame limits.

#Tuning

  • Rings too soft? Raise torus.sharpness for a crisper edge, or raise the quality tier. The core is only a few cells across on low (0.1 m cells).
  • Rings breaking up early? The vortexRings preset lowers physics.turbulence to 0.25 and damping to 0.02, so the air in the box stays still. Turbulence is noise forcing, and it tears rings apart.
  • Want to see the flow, not the torus? set({ torus: { density: 0 } }) leaves only the dye the fluid carries.
  • Rings on a timer from code: set interval: 0 and call fire() from your own events. fire(overrides) merges the overrides for that shot only.