Tobs248's picture
Develop an interactive room acoustics calculator webapp for analyzing room modes in audio spaces. Support both rectangular and L-shaped room configurations with dual input methods: direct numerical entry (length, width, height) and graphical drawing board for wall sketching. Enable interactive subwoofer placement within room layouts. Calculate axial, tangential, and oblique room modes using the formula f = (c/2) × (n/L) where c ≈ 343 m/s. Implement real-time visualization of room geometry, frequency response, mode distribution, and pressure mapping. Include responsive UI with live updates, supporting critical frequencies 20-200 Hz. Target audiophiles, studio planners, and acousticians seeking optimal subwoofer positioning and resonance identification for improved acoustic quality. implement a 3D Visualization with the possibility to change the positioning of the subwoofer via drag and drop
acb1e49 verified
Raw
History Blame Contribute Delete
5.12 kB
// Room Acoustics Calculator Logic
class RoomAcousticsCalculator {
constructor() {
this.speedOfSound = 343; // m/s
this.roomDimensions = { length: 5, width: 4, height: 3 };
this.subwooferPosition = { x: 2.5, y: 2, z: 1.5 };
this.modes = [];
this.init();
}
init() {
this.calculateModes();
}
setRoomDimensions(dimensions) {
this.roomDimensions = dimensions;
this.calculateModes();
}
setSubwooferPosition(position) {
this.subwooferPosition = position;
this.calculatePressureDistribution();
}
calculateModes(maxFrequency = 200) {
const { length, width, height } = this.roomDimensions;
this.modes = [];
// Maximum mode numbers based on max frequency
const maxN = Math.ceil((2 * maxFrequency * length) / this.speedOfSound);
const maxM = Math.ceil((2 * maxFrequency * width) / this.speedOfSound);
const maxP = Math.ceil((2 * maxFrequency * height) / this.speedOfSound);
for (let n = 0; n <= maxN; n++) {
for (let m = 0; m <= maxM; m++) {
for (let p = 0; p <= maxP; p++) {
if (n === 0 && m === 0 && p === 0) continue;
const frequency = (this.speedOfSound / 2) *
Math.sqrt(
Math.pow(n / length, 2) +
Math.pow(m / width, 2) +
Math.pow(p / height, 2)
);
if (frequency <= maxFrequency) {
let modeType = 'oblique';
if ((n > 0 && m === 0 && p === 0) ||
(n === 0 && m > 0 && p === 0) ||
(n === 0 && m === 0 && p > 0)) {
modeType = 'axial';
} else if ((n > 0 && m > 0 && p === 0) ||
(n > 0 && m === 0 && p > 0) ||
(n === 0 && m > 0 && p > 0)) {
modeType = 'tangential';
}
this.modes.push({
n, m, p,
frequency: parseFloat(frequency.toFixed(2)),
type: modeType
});
}
}
}
}
// Sort by frequency
this.modes.sort((a, b) => a.frequency - b.frequency);
return this.modes;
}
calculatePressureAtPosition(x, y, z) {
const { length, width, height } = this.roomDimensions;
let pressure = 0;
// Simplified pressure calculation based on mode shapes
this.modes.forEach(mode => {
const { n, m, p, frequency } = mode;
if (frequency >= 20 && frequency <= 200) {
// Mode shape function
const modeShape = Math.cos((n * Math.PI * x) / length) *
Math.cos((m * Math.PI * y) / width) *
Math.cos((p * Math.PI * z) / height);
// Pressure contribution (simplified)
pressure += Math.abs(modeShape) / (frequency * frequency);
}
});
return pressure;
}
calculatePressureDistribution() {
// This would normally generate a full 3D pressure map
// For now we'll just return pressure at subwoofer position
return this.calculatePressureAtPosition(
this.subwooferPosition.x,
this.subwooferPosition.y,
this.subwooferPosition.z
);
}
getCriticalFrequencies() {
const { length, width, height } = this.roomDimensions;
return [
{
name: "Length Axial",
frequency: parseFloat(((this.speedOfSound / 2) / length).toFixed(2)),
dimension: "length"
},
{
name: "Width Axial",
frequency: parseFloat(((this.speedOfSound / 2) / width).toFixed(2)),
dimension: "width"
},
{
name: "Height Axial",
frequency: parseFloat(((this.speedOfSound / 2) / height).toFixed(2)),
dimension: "height"
}
];
}
}
// Global calculator instance
const calculator = new RoomAcousticsCalculator();
// Utility functions
function updateResults() {
document.dispatchEvent(new CustomEvent('calculatorUpdated', {
detail: {
modes: calculator.modes,
criticalFrequencies: calculator.getCriticalFrequencies(),
pressure: calculator.calculatePressureDistribution()
}
}));
}
// Initialize when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
updateResults();
});
// Export for use in components
window.calculator = calculator;
window.updateResults = updateResults;