Feature: music visualization

This commit is contained in:
Dejvino 2025-11-22 00:04:01 +01:00
parent 383ba8baf1
commit 612a1bf501
7 changed files with 78 additions and 9 deletions

View File

@ -61,6 +61,15 @@ export class LightBall extends SceneFeature {
mesh.position.y = 10 + Math.cos(time * driftSpeed * 1.3 + offset) * naveHeight/2 * 0.6;
mesh.position.z = Math.cos(time * driftSpeed * 0.7 + offset) * length/2 * 0.8;
light.position.copy(mesh.position);
// --- Music Visualization ---
if (state.music) {
const baseIntensity = 4.0;
light.intensity = baseIntensity + state.music.beatIntensity * 3.0;
const baseScale = 1.0;
mesh.scale.setScalar(baseScale + state.music.beatIntensity * 0.5);
}
});
}
}

View File

@ -0,0 +1,39 @@
import { state } from '../state.js';
import { SceneFeature } from './SceneFeature.js';
import sceneFeatureManager from './SceneFeatureManager.js';
export class MusicVisualizer extends SceneFeature {
constructor() {
super();
sceneFeatureManager.register(this);
}
init() {
// Initialize music state
state.music = {
bpm: 120,
beatDuration: 60 / 120,
measureDuration: (60 / 120) * 4,
beatIntensity: 0,
measurePulse: 0,
};
}
update(deltaTime) {
if (!state.music) return;
const time = state.clock.getElapsedTime();
// --- Calculate Beat Intensity (pulses every beat) ---
// This creates a sharp attack and slower decay (0 -> 1 -> 0)
const beatProgress = (time % state.music.beatDuration) / state.music.beatDuration;
state.music.beatIntensity = Math.pow(1.0 - beatProgress, 2);
// --- Calculate Measure Pulse (spikes every 4 beats) ---
// This creates a very sharp spike for the torch flame effect
const measureProgress = (time % state.music.measureDuration) / state.music.measureDuration;
state.music.measurePulse = measureProgress < 0.2 ? Math.sin(measureProgress * Math.PI * 5) : 0;
}
}
new MusicVisualizer();

View File

@ -97,7 +97,7 @@ export class PartyGuests extends SceneFeature {
const time = state.clock.getElapsedTime();
const moveSpeed = 1.0; // Move slower
const movementArea = { x: 10, z: 30, y: 0, centerZ: 0 };
const movementArea = { x: 10, z: 30, y: 0, centerZ: 5 };
const jumpChance = 0.05; // Jump way more
const jumpDuration = 0.5;
const jumpHeight = 0.1;
@ -139,7 +139,12 @@ export class PartyGuests extends SceneFeature {
mesh.position.y = movementArea.y + guestHeight / 2;
}
} else {
if (Math.random() < jumpChance) {
let currentJumpChance = jumpChance * deltaTime; // Base chance over time
if (state.music && state.music.beatIntensity > 0.8) {
currentJumpChance = 0.1; // High, fixed chance on the beat
}
if (Math.random() < currentJumpChance) {
guestObj.isJumping = true;
guestObj.jumpHeight = jumpHeight + Math.random() * jumpVariance;
guestObj.jumpStartTime = time;

View File

@ -10,6 +10,7 @@ import { Stage } from './stage.js';
import { MedievalMusicians } from './medieval-musicians.js';
import { PartyGuests } from './party-guests.js';
import { StageTorches } from './stage-torches.js';
import { MusicVisualizer } from './music-visualizer.js';
// Scene Features ^^^
// --- Scene Modeling Function ---

View File

@ -86,11 +86,17 @@ export class StageTorches extends SceneFeature {
update(deltaTime) {
this.torches.forEach(torch => {
let measurePulse = 0;
if (state.music) {
measurePulse = state.music.measurePulse * 2.0; // Make flames jump higher
}
// --- Animate Particles ---
const positions = torch.particles.geometry.attributes.position.array;
for (let i = 0; i < torch.particleData.length; i++) {
const data = torch.particleData[i];
data.life -= deltaTime;
const yVelocity = data.velocity.y + measurePulse;
if (data.life <= 0) {
// Reset particle
@ -101,15 +107,21 @@ export class StageTorches extends SceneFeature {
} else {
// Update position
positions[i * 3] += data.velocity.x * deltaTime;
positions[i * 3 + 1] += data.velocity.y * deltaTime;
positions[i * 3 + 1] += yVelocity * deltaTime;
positions[i * 3 + 2] += data.velocity.z * deltaTime;
}
}
torch.particles.geometry.attributes.position.needsUpdate = true;
// --- Flicker Light ---
const flicker = Math.random() * 0.5;
torch.light.intensity = 2.0 + flicker;
const baseIntensity = 2.0;
const flicker = Math.random() * 0.6;
let beatPulse = 0;
if (state.music) {
beatPulse = state.music.beatIntensity * 1.5;
}
torch.light.intensity = baseIntensity + flicker + beatPulse;
});
}
}

View File

@ -235,10 +235,13 @@ export class StainedGlass extends SceneFeature {
update(deltaTime) {
// Add a subtle pulsing glow to the windows
const pulseSpeed = 0.5;
const minIntensity = 0.1; // Increased intensity for a stronger glow
const maxIntensity = 0.2;
const intensity = minIntensity + (maxIntensity - minIntensity) * (0.5 * (1 + Math.sin(state.clock.getElapsedTime() * pulseSpeed)));
let intensity = 0.15; // Base intensity
// --- Music Visualization ---
if (state.music) {
const beatPulse = state.music.beatIntensity * 0.3;
intensity += beatPulse;
}
// To make the glow match the vertex colors, we set the emissive color to white
// and modulate its intensity. The final glow color will be vertexColor * emissive * emissiveIntensity.