With jitter:

Without jitter:

Spherical harmonics:

Click and drag to rotate

To freeze animations, you can set the "Speed" slider to zero

If this doesn't work or runs slowly in your browser, you might want to try this older version.

Math notes

The wave equation for a function f is

ftt = ∇2f

where ∇2 is the Laplacian, and ftt is the second partial derivative of f with respect to time. The exact definition of the Laplacian depends on the space that f lives on, but the basic idea is that the Laplacian measures how concave or convex the graph of f is. On the real line, the Laplacian is just the ordinary second derivative used to determine whether f is concave up or down. In three dimensional Euclidean space, the Laplacian is ∇2f(x,y,z)=fxx+fyy+fzz.

Defining the Laplacian on a curved surface is a little trickier. If you want to get really fancy, you can use the metric on the surface to define the so-called Laplace-Beltrami operator. For the two-dimensional sphere S defined by x2+y2+z2 = 1, there is a much easier approach that doesn't require differential geometry. Given a real-valued function f on S, we can extend it to all of 3-space minus the origin as follows: using vector notation v = (x,y,z), let f(v) = f(v/|v|), where |v| = (x2+y2+z2)1/2. Then just use the regular Laplacian on 3-space from before. This works for higher-dimensional spheres as well.

The wave equation gets its name from modeling wave propagation on an elastic string or thin surface. The basic idea is that f measures displacement of the string or surface from equilibrium. If this displacement isn't too big, then up to a constant, ∇2f is a good approximation to the force on the medium caused by tension. (This constant can be thought of as the "spring constant" measuring elasticity or stiffness, which can be changed in the simulation by adjusting the "Elasticity" slider.) According to Newton's law, force is proportional to acceleration, which is ftt. Setting these two things equal gives the wave equation.

This program uses the numerical method from this paper to approximate the wave equation on a sphere. The most popular numerical methods for PDEs are finite difference methods that work by approximating functions with their values on a discrete grid. Derivatives at each point are approximated by looking at adjacent values on the grid. In this case we use a triangular mesh on the sphere made up of about 2,000 points. To approximate the Laplacian, the paper uses the divergence theorem from vector calculus (which is also true on surfaces) applied to the gradient of f, and the fact that the Laplacian is equal to the divergence of the gradient.

Special solutions of the wave equation can be built from spherical harmonics, real valued functions Y(v) on the sphere which are eigenfunctions of the Laplacian. Eigenfunction means that ∇2Y(v) = -λY(v) for some constant λ. If λ is positive, then it is easy to check that f(v,t) = cos(λ1/2t)Y(v) is a solution of the wave equation. Since Y(v) does not depend on time, f(v,t) will be a constant multiple of Y(v) at all times. Graphing f(v,t) gives a surface that pulses in and out but otherwise does not change shape. Checking that the solution has this behavior is one way to check whether a numerical method is accurate.

The buttons in the program let you try different spherical harmonics as initial conditions. The value of L on the button is defined in terms of the eigenvalue λ, while the M has to do with the fact that by definition, spherical harmonics are also eigenfunctions of another differential operator, an angular momentum operator used in quantum mechanics. (Spherical harmonics show up in the solution to Schrodinger's equation for an atom with one electron, and actually define the shape of the orbitals that you see in chemistry books. They are also related to the shape of the periodic table.) For some of the spherical harmonics, the shapes have the expected pulsing behavior, but for others, they gradually become distorted. This is interesting to watch, but means that the numerical method being used in the program is inaccurate. Since it was designed for speed rather than accuracy, this isn't too surprising. Another way to see inaccuracies is to note that the "Pulse" function starts out with an axis of symmetry that should be preserved by the wave equation, but quickly breaks down in the program.

It's interesting to think about why some of the harmonics are stable and others are not. While I don't know exactly what is going on, I think this is because the stable harmonics (the three buttons with L=2, M=0; L=2, M=1; and L=4, M=2) have a certain configuration that the others do not. We can look at all the points on the sphere where a given harmonic Y(v) equals zero. In general, this will be a configuration of lines. For the stable harmonics, at most two of the lines ever cross at a single point. All of the unstable harmonics, except for L=4, M=0, have points where three or more lines cross. This is not a stable configuration, because slightly wiggling any of the lines will turn the intersection point into three other intersection points where pairs of the lines cross instead of all three. The L=4, M=0 harmonic actually has no line crossings, but it could be that the lines are still too close together to remain stable with this mesh size.

Programming notes

In addition to the graphics rendering, this program does all the calculations for the finite difference method on the GPU. This is potentially faster than doing the calculations in JavaScript because the calculation for each gridpoint can be done in parallel, which GPUs are good at.

Running the FDM on the GPU is done with a custom vertex shader and fragment shader. In any WebGL program, the computer feeds arrays of data to the GPU, which are then processed with shaders. For this program, the position and velocity at each point on the sphere are stored in what's called a "float texture", an array of floating point numbers that can be accessed anywhere in the vertex or fragment shader. (Float textures are currently not built into core WebGL and require an extension.) Textures were originally used to map patterns onto 3D shapes, but thanks to shaders, the data can be used pretty much however you want. At each step of the FDM, the shaders calculate the new values and "render" them to another texture instead of the screen. A different vertex/fragment shader are used to draw what you see on the screen.

This method can be used to do a lot of interesting things with WebGL. As well as the many demos on Chrome Experiments, check out http://www.ibiblio.org/e-notes/webgl/gpu/contents.htm and http://flexi23.pisces.uberspace.de/fmx/?full#21.

There are some drawbacks with using the GPU for calculations. For instance, it's hard (at least with the current WebGL standard) to get data from a float texture back onto the CPU so you can do stuff with it in JavaScript. This can make it hard to add interactive features.