Ten more interactive camera families: bubbles, balloons, particle mirror, cloth, light painting, touch fluid, electric silhouette, swarm, tile wall, firewall
interactive.js becomes a multi-mode engine sharing camera, motion grid, gradient/centroid tracking and the mouse fallback. New renderers: point/line sprites (mirror/swarm/cloth/pops) and a ping-pong trail FBO (painting). fluid.js gains a 'touch' emitter mode fed by motion splats from the camera. Mirror mode reforms the custom SVG/image if loaded, else a DJ LUZA wordmark. UNPACK_ALIGNMENT set to 1 for non-multiple-of-4 single-channel uploads. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ea31d68350
commit
7e745743c5
4 files changed
+886
-154
No files matched your search
Vendored
+16
-5
@@ -54,10 +54,20 @@ const FAMILIES = [
|
||||
{ name: 'Tunnel Neon', scale: 1.0 },
|
||||
{ name: 'Tempesta Plasma', scale: 1.0 },
|
||||
{ name: 'Supernova', scale: 1.0 },
|
||||
// Camera-interactive family (interactive.js): webcam motion pushes physical
|
||||
// balls around the screen. Excluded from Auto VJ (turning the camera on must
|
||||
// stay a deliberate user choice).
|
||||
{ name: 'Interattivo Palle', scale: 1.0, interactive: true }
|
||||
// Camera-interactive families (interactive.js): webcam motion drives the
|
||||
// scene (interMode picks the behaviour). Excluded from Auto VJ (turning the
|
||||
// camera on must stay a deliberate user choice). Append-only.
|
||||
{ name: 'Interattivo Palle', scale: 1.0, interactive: true },
|
||||
{ name: 'Interattivo Bolle', scale: 1.0, interactive: true, interMode: 'bubbles' },
|
||||
{ name: 'Interattivo Palloncini', scale: 1.0, interactive: true, interMode: 'balloons' },
|
||||
{ name: 'Interattivo Specchio', scale: 1.0, interactive: true, interMode: 'mirror' },
|
||||
{ name: 'Interattivo Tessuto', scale: 1.0, interactive: true, interMode: 'cloth' },
|
||||
{ name: 'Interattivo Pittura', scale: 1.0, interactive: true, interMode: 'paint' },
|
||||
{ name: 'Interattivo Fluido', scale: 1.0, interactive: true, interMode: 'fluid' },
|
||||
{ name: 'Interattivo Silhouette', scale: 1.0, interactive: true, interMode: 'silhouette' },
|
||||
{ name: 'Interattivo Sciame', scale: 1.0, interactive: true, interMode: 'swarm' },
|
||||
{ name: 'Interattivo Piastrelle', scale: 1.0, interactive: true, interMode: 'tiles' },
|
||||
{ name: 'Interattivo Firewall', scale: 1.0, interactive: true, interMode: 'firewall' }
|
||||
];
|
||||
|
||||
// Palettes: low colour (a) -> high colour (b), plus optional hue-cycle/sat.
|
||||
@@ -117,7 +127,8 @@ function makeEffect(fi, pi, vi) {
|
||||
// setEffect can't leave a stale flag behind when switching presets.
|
||||
isFluid: fam.fluid ? 1 : 0,
|
||||
fluidMode: fam.fluidMode || 'ink',
|
||||
isInteractive: fam.interactive ? 1 : 0
|
||||
isInteractive: fam.interactive ? 1 : 0,
|
||||
interactiveMode: fam.interMode || 'balls'
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -279,6 +279,30 @@ class FluidSim {
|
||||
this._splat(0.06, 0.5 + 0.3 * Math.sin(t * 5.3), 1000 * (0.4 + bass), 0,
|
||||
c[0] * 0.5, c[1] * 0.5, c[2] * 0.5, 0.0018);
|
||||
}
|
||||
} else if (mode === 'touch') {
|
||||
// Camera-interactive (InteractiveSim): splats come from detected motion,
|
||||
// pushed along the direction the person is moving; a faint idle plume
|
||||
// keeps the screen alive when nobody moves.
|
||||
const list = this.extSplats || [];
|
||||
const FT = (300 + 420 * bass) * dt * 60;
|
||||
for (const s of list) {
|
||||
const c = dyeCol(s.ph);
|
||||
const k = Math.min(1, s.k * 1.8);
|
||||
this._splat(s.x, s.y, s.dx * FT * (0.6 + k), s.dy * FT * (0.6 + k),
|
||||
c[0] * inj * 9.0 * k, c[1] * inj * 9.0 * k, c[2] * inj * 9.0 * k,
|
||||
0.004 + 0.004 * k);
|
||||
}
|
||||
this.extSplats = null;
|
||||
if (!list.length) {
|
||||
const c = dyeCol(0.2);
|
||||
const x = 0.5 + 0.25 * Math.sin(t * 0.35);
|
||||
this._splat(x, 0.35, Math.sin(t * 0.9) * force * 0.3, force * 0.22,
|
||||
c[0] * inj * 1.2, c[1] * inj * 1.2, c[2] * inj * 1.2, rad * 1.2);
|
||||
}
|
||||
if (onBeat) {
|
||||
const c = dyeCol(0.6);
|
||||
this._splat(0.5, 0.5, 0, 0, c[0] * 0.3, c[1] * 0.3, c[2] * 0.3, 0.004);
|
||||
}
|
||||
} else {
|
||||
// 'ink': wandering brushes (the original mode)
|
||||
for (let i = 0; i < this.emitters.length; i++) {
|
||||
|
||||
+842
-149
File diff suppressed because it is too large.
Load diff
@@ -90,6 +90,9 @@ class Visualizer {
|
||||
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
|
||||
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
|
||||
// Also kept for the interactive "mirror" mode, whose particles reform
|
||||
// the custom image instead of the default wordmark.
|
||||
this.customSource = source;
|
||||
}
|
||||
|
||||
render(timeSec, audio) {
|
||||
@@ -98,6 +101,7 @@ class Visualizer {
|
||||
if (this.inter && !e.isInteractive) this.inter.suspend();
|
||||
if (e.isInteractive && window.InteractiveSim) {
|
||||
if (!this.inter) this.inter = new window.InteractiveSim(gl);
|
||||
this.inter.customSource = this.customSource || null;
|
||||
this.inter.render(timeSec, audio, e, this.canvas);
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user