Artificial Intelligence Generated 3D Sound Waves

Artificial Intelligence Generated 3D Sound Waves script details
Type
Typescript logo image
typescript
App Version
0.19.9
Visibility
public
Date Created
Mar 20, 2025, 9:57:50 AM
Last Edit Date
Mar 23, 2025, 8:25:50 PM

Script Details

The Code
createGUI(); const start = () => { // Get the Babylon.js scene (assuming a BitByBit context or similar setup) const scene = bitbybit.babylon.scene.getScene(); // Create the plane with increased subdivisions for even smoother waves const plane = BABYLON.MeshBuilder.CreateGround( "wavePlane", { width: 40, height: 40, subdivisions: 300, updatable: true }, // Increased from 300 to 400 scene ); // Plane Vertex Shader BABYLON.Effect.ShadersStore["neonVertexShader"] = ` precision highp float; attribute vec3 position; uniform mat4 worldViewProjection; varying vec2 vPos; void main() { gl_Position = worldViewProjection * vec4(position, 1.0); vPos = position.xz; } `; // Plane Fragment Shader BABYLON.Effect.ShadersStore["neonFragmentShader"] = ` precision highp float; varying vec2 vPos; void main() { float distance = length(vPos); if (distance > 20.0) { discard; } else { float normalizedDistance = distance / 20.0; vec3 blue = vec3(0.0, 0.0, 1.0); vec3 white = vec3(1.0, 1.0, 1.0); vec3 color = mix(blue, white, normalizedDistance); gl_FragColor = vec4(color, 1.0); } } `; // Create shader material for the plane const shaderMaterial = new BABYLON.ShaderMaterial( "neonShader", scene, { vertex: "neon", fragment: "neon" }, { attributes: ["position"], uniforms: ["worldViewProjection"] } ); shaderMaterial.backFaceCulling = false; plane.material = shaderMaterial; // Add glow layer const glowLayer = new BABYLON.GlowLayer("glow", scene); glowLayer.intensity = 1.5; bitbybit.babylon.scene.backgroundColour({ colour: "#ffffff", }); // Audio Setup const audioContext = new AudioContext(); let analyser, dataArray, gainNode; navigator.mediaDevices.getUserMedia({ audio: true }) .then((stream) => { const source = audioContext.createMediaStreamSource(stream); gainNode = audioContext.createGain(); analyser = audioContext.createAnalyser(); gainNode.gain.value = 5.0; analyser.fftSize = 2048; const bufferLength = analyser.frequencyBinCount; dataArray = new Uint8Array(bufferLength); source.connect(gainNode); gainNode.connect(analyser); console.log("Microphone connected!"); }) .catch((err) => { console.error("Microphone error:", err); alert("Please allow microphone access."); }); // Animation Parameters const numBands = 10; const amplitudeFactor = 6.0; // Increased from 4.0 for taller waves const baseFreq = 0.5; // Decreased from 1.0 for larger waves const maxRadius = 20; let rotationAngle = 0; // Random phases for waves const angles = []; for (let j = 0; j < numBands; j++) { angles[j] = Math.random() * Math.PI * 2; } scene.onBeforeRenderObservable.add(() => { if (!analyser || !dataArray) return; analyser.getByteFrequencyData(dataArray); // Compute amplitudes from audio const bandSize = Math.floor(dataArray.length / numBands); const amplitudes = []; for (let i = 0; i < numBands; i++) { let sum = 0; for (let j = 0; j < bandSize; j++) { sum += dataArray[i * bandSize + j]; } amplitudes[i] = (sum / bandSize / 255) * amplitudeFactor; } // Update plane vertices for wave effect const positions = plane.getVerticesData(BABYLON.VertexBuffer.PositionKind); if (!positions) return; const numVertices = positions.length / 3; const time = performance.now() / 1000; for (let i = 0; i < numVertices; i++) { const x = positions[i * 3]; const z = positions[i * 3 + 2]; const r = Math.sqrt(x * x + z * z); let yDisp = 0; if (r <= maxRadius) { const theta = Math.atan2(z, x); const scale = Math.pow(1 - r / maxRadius, 2); for (let j = 0; j < numBands; j++) { const freq = (j + 1) * baseFreq; const wave = Math.sin(freq * r + time * 0.5 + angles[j]) * Math.cos((j + 1) * theta); yDisp += amplitudes[j] * wave; } yDisp *= scale; } positions[i * 3 + 1] = yDisp; } // Rotate the plane rotationAngle += 0.005; plane.rotation.y = rotationAngle; plane.updateVerticesData(BABYLON.VertexBuffer.PositionKind, positions); plane.refreshBoundingInfo(); }); } function createGUI() { const gui = bitbybit.babylon.gui; const textOpt = new Bit.Inputs.BabylonGui.CreateFullScreenUIDto(); const fullScreenUI = gui.advancedDynamicTexture.createFullScreenUI(textOpt); const stackPanelOpt = new Bit.Inputs.BabylonGui.CreateStackPanelDto(); stackPanelOpt.background = "#00000000"; const stackPanel = gui.stackPanel.createStackPanel(stackPanelOpt); const stackPanelOpt2 = new Bit.Inputs.BabylonGui.CreateStackPanelDto(); stackPanelOpt2.background = "#00000000"; const stackPanel2 = gui.stackPanel.createStackPanel(stackPanelOpt2); stackPanel2.verticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_BOTTOM; fullScreenUI.addControl(stackPanel2); stackPanel.paddingTopInPixels = 10; stackPanel2.addControl(stackPanel); const buttonOptions = new Bit.Inputs.BabylonGui.CreateButtonDto(); buttonOptions.width = "400px"; buttonOptions.height = "150px"; buttonOptions.label = "START EXPERIENCE"; buttonOptions.color = "#ffffff"; buttonOptions.background = "#00000000"; const button = gui.button.createSimpleButton(buttonOptions); button.paddingTopInPixels = 20; button.paddingBottomInPixels = 20; button.onPointerClickObservable.add(() => { start(); }) const imageOpt = new Bit.Inputs.BabylonGui.CreateImageDto(); imageOpt.url = "assets/logo-gold-small.png"; const image = gui.image.createImage(imageOpt); const padding = 30; image.paddingTopInPixels = padding; image.paddingBottomInPixels = padding; image.paddingLeftInPixels = padding; image.paddingRightInPixels = padding; image.onPointerClickObservable.add(() => { window.open("https://bitbybit.dev", '_blank').focus(); }); const txtBlockOptions1 = new Bit.Inputs.BabylonGui.CreateTextBlockDto(); txtBlockOptions1.text = "Powered By"; const txtBlock1 = gui.textBlock.createTextBlock(txtBlockOptions1); txtBlock1.height = "40px"; txtBlock1.color = "#ffffff"; txtBlock1.paddingBottomInPixels = 10; const txtBlockOptions = new Bit.Inputs.BabylonGui.CreateTextBlockDto(); txtBlockOptions.text = "BITBYBIT.DEV"; const txtBlock = gui.textBlock.createTextBlock(txtBlockOptions); txtBlock.height = "60px"; txtBlock.color = "#ffffff"; txtBlock.paddingBottomInPixels = 30; stackPanel2.addControl(button); stackPanel2.addControl(image); stackPanel2.addControl(txtBlock1); stackPanel2.addControl(txtBlock); }