Category Archives: JavaScript

I have been researching beam tracing for a project of mine for a while now. Beam tracing is a method for calculating reflection paths. I won't go into any details of how beam tracing works, as this is something you can find on google in case you're not familiar with it.

My first attempt at beam tracing was made in Python, about a year ago. I made an algorithm that worked in 3D, but it became exceedingly complicated as I worked on it (mostly related to occlusion and clipping). As it turned out, I was overcomplicating things. The paper "Accelerated beam tracing algorithm" by Laine et al. served as an excellent introduction into how basic beam tracing should be done: as simply as possible. To get a better understanding of the paper, I decided to try to implement some of the very basics in 2D using JavaScript.

The result

Click to place the source, the specular reflections are updated automatically as you move the mouse around. Note that reflections are only calculated up to a certain limit (theoretically there are an infinite amount of reflections).

Firstly, it should be noted that I only utilised the very basic ideas presented in the paper in my implementation (beam trees and accelerated ray tracing). I only wanted to spend a few days on this, which means that there are probably a lot of things which could (and should) be optimised. I spent most of the time pruning out bugs, which at times were very hard to find. I also pushed the MIT-licensed source code to Bitbucket, in the hopes that someone might think of something useful to do with 2D beam tracing in JavaScript!

I got the idea to try the topic of this blog post during the holidays. It's nothing new (cross-talk cancellation), but interesting nonetheless. The basic principle is relatively simple, which makes it a fascinating example of applied physics!

Note that you need speakers, preferably ones which you can place quite close to each other and in front of you. A laptop should generally work nicely. Also, this doesn't work on internet explorer. Then follow these steps and fill in the information below:

Measure the distance between your speakers (middle-to-middle, A).

Measure the distance from your ears to the middle point between your speakers (B).

Make sure your head is in the middle, as in the picture below, and at about the same distance as when you measured the distance B.

Click "Left" or "Right"!

Distances to measure

Try it out!

See "Troubleshooting" if nothing special seems to be happening.

Troubleshooting

What should be happening: you should see a user interface, in which you can play a sound by clicking "Left" or "Right". The sound should shift to the far right or far left when phase cancellation is enabled, even when the speakers are right in front of you.

There are some problems related to the audio playback-part of the demonstration. It doesn't seem to work on a lot of systems (even if you're not using internet explorer). If the sound is more like a sharp bell than a soft bell, something went wrong and the effect won't be that great, sorry. This means that the sound is distorted. I might fix this at some point, but it seems to be related to the Web Audio API / pico.js somehow, so it's probably not an easy fix.

Some other stuff that might affect the result:

The measurements need to be fairly accurate.

Make sure you're not close to any walls.

Too much reverberation will impair the effect.

Sit in the middle, relative to the speakers, and look straight ahead.

Some speakers distort the sound when you turn the volume up (my laptop does), so try lowering the volume if the demonstration doesn't work. The audible sound should be as close to a sine wave (smooth) as possible.

There will be problems when you raise the frequency enough. Try 400 Hz, it worked nicely for me.

The effect is more pronounced when the speakers are close to each other and in front of you (like in a laptop); the sound will appear to be coming from a totally different direction.

If your speakers already form a wide stereo field, the effect isn't that noticeable

What was that?

Let's start by considering what makes the sound appear to be coming from the direction of a speaker, in general. The two most important cues which allow you to deduce the direction of the sound are related to how the sound arrives at our ears; the sound reaches each ear at a different time and at a different volume. People usually talk about the interaural level difference (ILD) and the interaural time difference (ITD). The sound usually has a different distance to travel for each ear, so the sound will arrive a little bit later at the other ear. It will also arrive at a slightly lower volume. In our case, the ITD and ILD are approximated using the distances in the image below.

Sound source - ears, distance

Now let's introduce the concept of phase cancellation. This is something which active noise cancelling headphones use, for example. Sound consists of pressure changes. If we can align positive and negative pressure changes in the same point in space, they sum up to zero. The basic idea is explained in the following image:

Phase cancellation [wiki]

OK, let's now combine the two previous principles! We want to play a sound from the right speaker, but we want to cancel out the sounds arriving at the left ear. How do we do this?

Cross-talk cancellation

A

We play the sound using the right speaker

We play another sound (with inverted phase) using the left speaker. We time the signal so that the sound from the right speaker and the sound from the left speaker arrive at the left ear simultaneously. They cancel each other out at the left ear.

B + C

There's still a problem; what will happen when the sound with the inverted phase (image A) arrives at the right ear (image B)? This will cause problems, we don't want anything to arrive at the right ear after the first signal! So what do we do? We cancel it out, once more, using a signal with the correct phase (image B).

But, once more, the signal we played in image B needs to be cancelled out at the left ear (image C). Well, I hope you get the picture. This goes on and on for a while. Luckily, we won't have to do this forever, as the volume of the phase cancellation signal decreases with time.

Epilogue

I think this is a cool example which demonstrates the wave nature of sound. It's intuitive and relatively simple, yet gives results anyone with proper hearing can observe (assuming the demonstration works on your setup).

I recently programmed a method for easily doing basic 2D finite element analysis of acoustics. I did this for a different kind of project, but thought it would be cool to try it out as a method of analyzing how sound behaves in L-shaped rooms. I used the following dimensions in the software (the room consists of the L-shape in the picture):

Room dimensions

Obviously we're simplifying things a lot, as we're leaving the height of the room out of the calculations. Still, the calculations will give us a lot of information of how sound behaves from the perspective of the most interesting dimensions of the room.

Let's try feeding in a plane wave from the top of the room, just to see what happens. Try moving the slider around a bit to see how the sound field forms in the room. Please note that it can take a while for the content to load.

Some central things to note:

When the plane wave reaches the convex corner, the corner will radiate sounds in all directions (also to the right).

A sound field quickly forms in both the top-down and the left-right direction

The red dot represents a microphone in the room, in case you're wondering. Let's see what the microphone gives us:

The signal arriving at the microphone in the room

We can clearly see when the first diffracted sound arrives at the microphone. Two reflections arrive shortly afterwards, in close succession. After that, the sound field quickly becomes complicated.

Let's check out the frequency response as measured by the microphone:

Frequency response as measured by the microphone

We can clearly see at least a few room modes. Let's try examining the modes more thoroughly.

Additional: The plane wave

The plane wave consists of a gaussian pulse. We can't feed too sharp of a pulse into the room, as that would lead to errors in the calculations. By increasing the width of the pulse, we can get a pulse which the calculations will be able to handle.

Additional: How is the response of the room calculated

I cheated a bit. The calculation model I used doesn't account for damping, which in practice means that the room would continue reverberating indefinitely. I calculated the response for 0.5 seconds and approximated damping simply by multiplying the non-fading response with a decaying curve. Which isn't something that really should be done.

The gaussian pulse has the following frequency content (magnitude):

Using this, I calculated the response up to 200 Hz by deconvolving the pulse from the response. Deconvolution, in this case, means that I took into account the varying frequency content of the excitaiton. This can be done by dividing the frequency content of the response by the frequency content of the excitation.

Room modes

I've written quite a bit about room modes recently, but not from the perspective of irregular rooms. Let's see what they look like in this case! Below are the 6 lowest room modes. Many of them are far from obvious, as can be seen. They bear very little resemblance to the lowest room modes in a rectangular room.

Room mode at 32 Hz

Room mode at 49 Hz

Room mode at 76 Hz

Room mode at 80 Hz

Room mode at 86 Hz

Room mode at 91 Hz

Conclusions

If the geometry of the room differs even slightly from a rectangular layout, and one wishes to calculate an approximation of the room modes of the space, there really doesn't seem to be that many options available. Numerical modeling using the finite element method, as used in this post, is a method which works nicely.

The simulation

Note that when you move the source or change the frequency of the source, it will take a while for the diffraction calculations to update.

I recommend that you keep the source to the left of the wedge if you want to observe the results of the diffraction calculations, as the calculations are done starting to the right of the wedge (you'll see what I mean if you play around with the simulation).

I could make the code much more effective by optimizing things (many times more effective, probably), but I won't do any of that as it's just a proof of concept.

The theory

The specular reflections are calculated by reflecting the source relative to the wedge. The shadowing is self-explanatory.

The diffraction is calculated using the Biot-Tolstoy expressions, using the method presented by Svensson et al (JASA 1999). The simulation spans 1 meter by 1 meters. The wedge spans 1 meter to both sides of the source. The simulation represents a cut plane perpendicular to to the wedge.

The amplitude and phase of the diffracted signal is calculated from the impulse response at many points in space, using the Goertzel algorithm (for a single frequency). The algorithm is implemented server-side, using Python/Numpy.

The calculations are done with some simplifications, to keep the server happy. I haven't validated the results in the simulation thoroughly, small errors could be seen for the few points I tested (most likely due to the simplifications). But the calculation model works; I compared my Python implementation more rigorously with the examples in the paper by Svensson.

The implementation

A lot of things are calculated using the shaders which definitely should not be calculated using them. If you delve deeper into the code (esp. the shaders), you will most definitely encounter quite a few ugly things. But the simulation runs happily on my computer(s), so I'm happy.

Each time you move the source or change the frequency, the contribution of the diffracted signal is calculated for a polar grid, spanning 32 x 32 points (it extends quite a bit outside the visible view). The origin of the grid is at the edge of the wedge. The calculations are done server-side, and fetched one data point at a time using jQuery/ajax/JSON. This makes the calculations really slow, but it's partly also because I don't want to strain the server too much. I mainly wanted to test how these types of calculations can be done using Django.

This diffraction data is passed to the shaders using a 2D texture, with two bytes ("red" and "green") representing the amplitude of the signal and one byte ("blue") representing the necessary phase data.

WebGL can handle linear interpolation for textures automatically, so the data is interpolated nicely in-between the data points.