<!DOCTYPE html>
<html>
<head>
<title>3D Music sequencer</title>
<meta name=“viewport” content=“width=device-width, initial-scale=1”>
<link rel=“stylesheet” href=“https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css”>
<script src=“https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js”></script>
<script src=“https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js”></script>
<script src=“https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js”></script>
<script src=“https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js”></script>
<script src=“https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.30/Tone.js”></script>
<style>
canvas {
display: block;
}
</style>
</head>
<body>

<div class=“container”>
<h1>3D Music Sequencer</h1>
<p>Click on the cubes to create your own music sequence:</p>
<div id=“container”></div>
</div>

<script>
// Set up the 3D scene
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.getElementById(‘container’).appendChild(renderer.domElement);

// Add directional light
const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
dirLight.position.set(1, 1, 1);
scene.add(dirLight);

// Add ambient light
const ambientLight = new THREE.AmbientLight(0x303030);
scene.add(ambientLight);

// Set up a grid of cubes
const geometry = new THREE.BoxGeometry();
const materials = [
new THREE.MeshBasicMaterial({ color: 0xff0000 }),
new THREE.MeshBasicMaterial({ color: 0x00ff00 }),
new THREE.MeshBasicMaterial({ color: 0x0000ff })
];
const gridWidth = 9;
const gridHeight = 9;
const gridDepth = 9;

const cubes = [];
for (let x = 0; x < gridWidth; x++) {
for (let y = 0; y < gridHeight; y++) {
for (let z = 0; z < gridDepth; z++) {
const cube = new THREE.Mesh(geometry, materials[x % 3]);
cube.position.set(x * 2 - gridWidth, y * 2 - gridHeight, z * 2 - gridDepth);
cube.userData.active = false;
cubes.push(cube);
scene.add(cube);
}
}
}

// Set initial camera position
camera.position.z = 25;

// Set up raycaster for selecting cubes
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();

// Set up Tone.js synthesizers
const synth = new Tone.PolySynth().toMaster();

// Animate the scene
const animate = function () {
requestAnimationFrame(animate);

// Rotate the camera around the grid
camera.position.x += (mouse.x * 50 - camera.position.x) * 0.02;
camera.position.y += (-(mouse.y * 50) - camera.position.y) * 0.02;
camera.lookAt(scene.position);

renderer.render(scene, camera);
};

animate();

// Handle window resize
window.addEventListener(‘resize’, function () {
const width = window.innerWidth;
const height = window.innerHeight;
renderer.setSize(width, height);
camera.aspect = width / height;
camera.updateProjectionMatrix();
});

// Handle mouse click
document.addEventListener(‘click’, function (event) {
event.preventDefault();

// Calculate mouse position in normalized device coordinates
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

// Update the raycaster with the new mouse position
raycaster.setFromCamera(mouse, camera);

// Calculate selectables in the 3D scene
const intersects = raycaster.intersectObjects(scene.children);

// Toggle cube selection state
if (intersects.length > 0) {
const selected = intersects[0].object;
selected.userData.active = !selected.userData.active;

if (selected.userData.active) {
selected.material.color.setHex(0xffff00);
const note = C${Math.floor(Math.random() * 3) + 3};
setTimeout(function () {
synth.triggerAttackRelease(note, ‘4n’);
}, 100);
} else {
selected.material.color.setHex(selected.material.color.getHex() ^ 0xffff00);
}
}
});

// Handle mouse movement
document.addEventListener(‘mousemove’, function (event) {
event.preventDefault();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
});

const