The Quest to Finding Chladni Patterns, Part 1: Theory and Algorithms
Introduction
Background and Mathematical Formulation
Take a solid metal plate (or any other rigid material) and sprinkle some particles on it. It can either be sand, salt, or even baking soda. Then, generate some vibration on the plate by using either a violin bow to excite the plate, or place it on a wave generator and set it to specific frequencies. What you will observe are the beautiful patterns generated as shown in the figure above. This experiment sounds relatively simple, but as simple as it sounds, this experiment reveals some extremely complex and intensive underlying mathematical principles. In fact, the problem of understanding these patterns challenged some of the brightest mathematical minds of the time, and the research to understand these patterns spans for hundreds of years, and still continues till the modern day.
How does this work? Well, it turns out these are examples of standing waves in the two-dimensional space. In one dimension, a “node” in a standing wave is a point along the wave where the amplitude is at minimum. Similarly, in two dimensions, there are “nodal lines,” which are static points on the plate where the particles settle.
In the spring semester of 2024, my physics project required selecting an experiment to demonstrate a scientific law. Chladni's law was a compelling choice, and one good idea was to prove the constant wave speed across a plate using the two-dimensional wave equation model
where $c$ here denotes the wave speed of the plate, $u_{tt}$ is the plate's displacement, and $Q$ is the forcing function (the wave generator).
By assuming that the plate is supported by tension, one can derive the two-dimensional wave equation in the Cartersian coordinate to be
The circular plate is similar, with a slightly different Laplacian since we need to convert the wave equation to the polar coordinate system:
In the next grand section, we will discuss the simulation process of this model on the square and circular domain, and to discuss the flaws of this model (lots of flaws, but no spoiler yet). Basically, the emphasized, initial goal of this project is to simulate Chladni patterns, prove that the wave speed is constant, and validate Chladni's law (see the section Chladni's Law below). For a much more detailed explanation, please refer to my project report and checkout the source code on github
Read more: Historical Notes
The study of wave and vibration theory dates its way back to the time of Pythagoras in the fifth century BC, where the Ionian School of Natural Philosophy first introduced the scientific methods to study the real world and natural phenomena. But just as with many other scientific discoveries before Newton, we lacked the tools to rigorously understand the underlying theory behind them. Fast forward to the Renaissance period. Galileo also noticed that pieces of bristle on the sounding board only move in certain areas. Leonardo da Vinci also observed the strange patterns generated by particles in response to vibration.
The first rigorous investigation of these patterns so called “Chladni patterns” is of course, attributed to German physicist and musician Ernst Chladni (1756 - 1827) (not Euler or Gauss this time). The idea of sprinkling sand on a plate and bow it is not entirely new, but Chladni took our understanding to a new level by coming up with a formula that describe the pattern, and hypothesized a formula that predicts the number of rings on the circular plate. This formula is later proven true by Kirchoff and Rayleigh.
Chladni's Law
Part of the formula states that the vibrating frequency is proportional to four times the number of rings on the plate squared, that is, $f \sim 4n^2$, where $n$ is the number of rings (see in Fig. 4). The full formula that he derived is \begin{equation} f \sim (m+2n)^2, \end{equation} where $m$ here is the number of diameter lines. This is called Chladni's law. We don't see any $m$'s in the figure below, there is a reason for this, and I'll get to that part later.
The Wave Equation Model
The general model and the boundary condition
Depends on whether you clamp your edge or not, clamp the edge partially, or simply support the plate by placing some other objects below the plate; the boundary condition may be different. For this project, we will emphasize the free boundary condition. That is, only the wave generator supports the plate.
| Type of Boundary | Boundary Condition |
|---|---|
| Clamped (fixed) edge | $u(\pm a, y, t) = u(x, \pm a, t) = 0$ |
| Moving edge | $u(a, y, t_0) = f(y, t)$ |
| Free edge | $\pd{u}{x}(\pm a, y, t) = \pd{u}{y}(x, \pm a, t) = 0$ |
The free boundary condition means that there are no forces acting on the edges of the plate; that is, expressed mathematically,
which gave us four boundary conditions. The uniqueness of the solution requires our equation to have two more initial conditions. Similar to ordinary differential equations, these conditions are the initial displacement and velocity of the plate at $t=0$. That is,
The Square Plate
Consider the wave equation on a rectangle $\Omega = \{(x,y)\in\R^2\;|\; -a\leq x,y\leq a\}$,
Here, $a$ is half the length of the plate, and also note that we denote the length of the plate to be $L$. A standard way to solve a forced differential equation is to solve its homogenous case first, i.e. when $Q=0$.There are many ways to solve this initial-boundary value problem, and one common, and easy-to-understand technique is via separation of variables. If we assume that $u$ is separable and let $u(x,y,t) = X(x)Y(y)T(t)$, the equation becomes
Divide both sides by $XYT$, we obtain
Notice that $\frac{X''}{X}+\frac{Y''}{Y}=-\lambda$ is constant if and only if $X''/X=-\mu$ and $Y''/Y = -\nu$, where $\mu$ and $\nu$ are themselves constants and $\lambda = \mu + \nu$. The two constants above yield two eigenvalue problems with Neumann boundary conditions:
and the time equation yields the third eigenvalue problem:
The detail solution of these eigenvalue problems is beyond the scope of this article. For the detail solution, again, please refer to my project report. The general idea is we need to solve the eigenvalue problems (\ref{eq:X1}) and (\ref{eq:Y1}) first, and from there we solve the third eigenvalue problem, since $\lambda = \mu + \nu$. After carrying out all the hard work, the eigenvalues for equations (\ref{eq:X1}) and (\ref{eq:Y1}) are
and the solution to the homogeneous equation is the double Fourier series
where,
The coefficients $a_n$ and $\overline{a_n}$ are defined as $a_n = \frac{1 + (-1)^n}{2},\overline{a_n} = \frac{1 - (-1)^n}{2}$, and,
The forcing term $Q$ doesn't change the overall shape of the patterns, but it does change the displacement of the plate overtime, hence the time function $T_{nm}(t)$ becomes
Here, $q_{nm}$ is a function defined on the disk of radius $r$ — the radius of the tip of the wave generator,
The Circular Plate
Consider the polar wave equation on a disk $\Omega = \{(r,\theta)\in\R^2\;|\; 0 \leq r\leq a, 0 \leq \theta\leq 2\pi\},$
Let $u(r, \theta, t) = R(r)\Theta(\theta)T(t)$. Skipping all the hard work., we may arrive at the final solution to be
Designing the Plotting Algorithms
Algorithm for the Square Plate
The reason for all those derivations above is to plot our theoretical patterns. Recall that our final solution is derived as
Before we design our algorithm, there are a few things to note:
-
In realistic experimentation, there is no way to tell what the
initial conditions $f(x,y)$ and $g(x,y)$ are. However, we can
assume that the initial velocity, $u(\mathbf{x},0) =
g(x,y)$ is zero. Moreover, the forcing term is non-zero only when
$n=m$, and it approaches zero as $\omega\to\infty$. This means
that it does not matter that much for the overall shape of the
plot, but it only affects the insignificant microscopic height of
the plate. The solution is now simplified to
\begin{equation} u(\mathbf{x}, t) = \sum_{n\geq 0}\sum_{m\geq 0}X_n(x)Y_m(y)A_{nm}\cos(\omega t). \end{equation}
- For the initial condition $f(x,y)$, we can pick a function $f(x,y)$ such that $|A_{ij}|\neq |A_{nm}|$, for any $(i, j)\neq (n,m)$. The reason why we can do this is that the realistic vertical displacement of the plate is extremely small, so the choice of $f$ doesn't matter that much. We simply choose the function $f$ that satisfies $|A_{ij}|\neq |A_{nm}|$ for the clearest patterns.
-
What we are really interested in is the shape of the nodal lines.
Therefore, for the sake of simplicity, we can choose certain time
points such that $\cos(\omega t) = 1$, that is, $t = 0,\;
2\pi/\omega,\; 4\pi/\omega,\ldots$. In this way, if we set
$u(\mathbf{x}, t) = 0$, the coefficients
$A_{nm}$ will be cancelled out, leaving us with the
contour equation
\begin{equation} u(\mathbf{x}, t) = \sum_{n\geq 0}\sum_{m\geq 0}X_n(x)Y_m(y) = 0. \end{equation}
The final step is to notice the angular frequency $\omega$. Our solution $u(\mathbf{x}, t)$ is derived as the superposition sum of all other possible solutions. However, this is a double infinite series; how are we going to plot this function? This is a lesson I learned throughout my time as an engineering student: Occasionally, we must reinterpret abstract mathematics to make it meaningful in the context of the physical world..
-
Recall that we derived our angular frequency to be
\begin{equation} \omega = \frac{\pi c}{L}\sqrt{n^2 + m^2}. \end{equation}What we are really interested in is not the general super-position solution, but solutions at certain frequencies $\omega$. After all, each frequencies must only correspond to some certain shape of the plate. This means that each $\omega$ must correspond so some combination of integer pairs $n$ and $m$.
-
For example, if $\omega = \frac{\pi
c}{L}\sqrt{5}$, then all possible
combinations of $n$ and $m$ must be $(1, 2)$ and $(2, 1)$ (since
$1^2 + 2^2 = 5$). Notice previously that we use the coefficients
$|A_{nm}|$ in absolute value, so negative coefficients
are possible. The two equations that describe the Chladni pattern
found at this frequency are
\begin{equation} X_1(x)Y_2(y) \pm X_2(x)Y_1(y)=0, \end{equation}For equations with more sum of squares combinations, there will be more combinations for a single mode. For example, for $\omega = \frac{\pi c}{L}\sqrt{65}$, all the possible implicit functions are\begin{equation} X_1(x)Y_8(y) \pm X_4(x)Y_7(y) \pm X_7(x)Y_4(y) \pm X_8(x)Y_1(y) =0\ \end{equation}We need not consider the first term due to symmetry. In general, if there are $N$ terms in the equation, there will be a total of $2^{N-1}$ possible plots.
- Special Case:
If $n^2 + m^2$ is a perfect square ($1, 4, 9, 16, 25,\ldots$), then
we also need to consider the pairs with 0. For example, if $n^2 + m^2
= 25$, then besides the standard pairs of $(3, 4)$ and $(4, 3)$, we
also need to consider the pairs $(0, 5)$ and $(5, 0)$. This is because
since we don't know what $f(x,y)$ is, we also don't know whether $A_{00}$
is 0. If it's zero, then we just plot the regular pairs $(3, 4)$ and
$(4, 3)$,
\begin{equation} X_3(x)Y_4(y) + X_4(x)Y_3(y)=0,\quad\text{and}\quad X_3(x)Y_4(y) - X_4(x)Y_3(y)=0. \end{equation}If $A_{00}$ is not zero, meaning that the summation actually starts at index $(n,m)=(0,0)$, then we also need to consider eight other patterns\begin{equation} X_0(x)Y_5(y) \pm X_3(x)Y_4(y) \pm X_4(x)Y_3(y) \pm X_5(x)Y_0(y) =0. \end{equation}In total, for the case of $S = 25$, there will be a total of $2+8=10$ plots.
In summary, the algorithm to plot the patterns of the square plate is just an algorithm to search for pairs of sum of squares $(n, m)$.
Algorithm 1: Square Plate Chladni Pattern Generation Function GenerateSignCombinations(pairs)
Input: List of integer pairs (n, m)
Output: Array of sign combinations [+/-1, +/-1, ...] with first element fixed at +1
Function BasisFunction(x, n)
Input: Spatial coordinate x, mode number n
Output: X_n(x) = a_n cos(n*pi*x/L) + a_bar_n sin(n*pi*x/L)
Return ((n+1) mod 2) * cos(n*pi*x/2) + (n mod 2) * sin(n*pi*x/2)
Function WaveFunction(x, y, pairs, signs)
Input: Coordinates (x, y), mode pairs [(n_1,m_1), (n_2,m_2), ...], sign array
Output: u(x,y) = sum over i of signs_i * X_n_i(x) * Y_m_i(y)
Return sum from i=1 to n of signs_i * BasisFunction(x, n_i) * BasisFunction(y, m_i)
Input: Integer S
Output: Set of pairs {(n, m) : n^2 + m^2 = S}
pairs = empty_set
for n = 0 to floor(sqrt(S)) do
m = sqrt(S - n^2)
if m is an integer then
pairs = pairs union {(n, m), (m, n)}
return pairs
Function IsSumOfSquares(S)
Input: Integer S
Output: Boolean indicating if S = n^2 + m^2 for some integers n, m
Return size(FindSumOfSquaresPairs(S)) > 0
Algorithm PlotSquareChladniPatterns(startIndex, endIndex)
Input: Range [startIndex, endIndex] for pattern indices
Output: Contour plots of nodal lines at u(x,y) = 0
totalPlots = endIndex - startIndex
equations = [ ] // Store wave functions
sumValues = [ ] // Store corresponding S values
plotCount = 0
k = 1
while plotCount < totalPlots do
if IsSumOfSquares(k) then
pairs = FindSumOfSquaresPairs(k)
signCombos = GenerateSignCombinations(pairs)
for each combo in signCombos do
if plotCount >= startIndex and plotCount < endIndex then
u_xy = WaveFunction(x, y, pairs, combo)
equations.append(u_xy)
sumValues.append(k)
plotCount = plotCount + 1
k = k + 1
for i = 0 to totalPlots - 1 do
AssignColorsByModePairs(pairs[i])
PlotContour(equations[i], level=0) // Plot nodal lines at z = 0
Algorithm for the Circular Plate
The circular plate is a bit easier, since we don't need to design an algorithm to search for sum of squares and perfect squares exceptions. Recall that for a circular plate,
By applying a similar technique as the square plate, we can eliminate $c_{nm}, d_{nm}$, and the forcing term. The circular plate is easier to implement because we don't have to search for any sum of squares, so each Chladni pattern is simply a pair of arbitrary $n$ and $m$. The equations that we need to plot are simply
Function BesselDerivativeZero(n, m)
Input: Order n, zero index m
Output: m-th zero z_nm of J'_n(x)
if n = 0 then
zeros = ComputeBesselPrimeZeros(0, m + 1)
zeros[0] = 0 // Redefine first zero as origin
return zeros[m]
else
zeros = ComputeBesselPrimeZeros(n, m + 1)
return zeros[m]
Function InitialDisplacement(r, theta)
Input: Polar coordinates (r, theta)
Output: Initial condition f(r, theta)
Return r * cos(theta)
Function FourierCoefficient_A(n, m, a)
Input: Mode numbers (n, m), plate radius a
Output: Coefficient a_nm from initial conditions
k = z_nm / a
numerator = integral from 0 to a, integral from 0 to 2*pi of
J_n(kr) * cos(n*theta) * f(r,theta) * r d(theta) dr
denominator = 2*pi * integral from 0 to a of J_n^2(kr) * r dr
Return numerator / denominator
Function FourierCoefficient_B(n, m, a)
Input: Mode numbers (n, m), plate radius a
Output: Coefficient b_nm from initial conditions
k = z_nm / a
numerator = integral from 0 to a, integral from 0 to 2*pi of
J_n(kr) * sin(n*theta) * f(r,theta) * r d(theta) dr
denominator = 2*pi * integral from 0 to a of J_n^2(kr) * r dr
Return numerator / denominator
Function CircularWaveFunction(n, m, r, theta, a)
Input: Mode numbers (n, m), polar coordinates (r, theta), radius a
Output: u_nm(r, theta) = J_n(kr)[a_nm cos(n*theta) + b_nm sin(n*theta)]
z_nm = BesselDerivativeZero(n, m)
k = z_nm / a
a_nm = FourierCoefficient_A(n, m, a)
b_nm = FourierCoefficient_B(n, m, a)
Return J_n(kr) * [a_nm * cos(n*theta) + b_nm * sin(n*theta)]
Algorithm PlotCircularChladniPattern(n, m, a)
Input: Mode numbers (n, m), plate radius a
Output: Contour plot of nodal lines at u(r,theta) = 0
for each point (r, theta) in [0, a] x [0, 2*pi] do
u[r, theta] = CircularWaveFunction(n, m, r, theta, a)
PlotContour(u, level=0) // Plot nodal lines where u = 0
The next part of this article will be dedicated to implementing the code in python and compare the theoretical results to the experimental result. By then, we may know whether the wave speed is constant or not. You can read Part 2 by pressing the directory below.
Appendix
Animation Code
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from mpl_toolkits.mplot3d import Axes3D
# Define the function f
def f(x, y, t):
return 0.2 * np.cos(t) * (
np.cos(2 * np.pi * x / 2) * np.cos(4 * np.pi * y / 2) +
np.cos(4 * np.pi * x / 2) * np.cos(2 * np.pi * y / 2)
)
# Create grid for x, y
x = np.linspace(-1, 1, 40)
y = np.linspace(-1, 1, 40)
X, Y = np.meshgrid(x, y)
# Create the figure and axis for the 3D animation
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
# Function to update the 3D surface for each frame
def update_surface(t):
ax.clear() # Clear previous plot
# Compute the Z values
Z = f(X, Y, t)
# Plot the dynamic surface
surface = ax.plot_surface(X, Y, Z, cmap='viridis', alpha=0.85, rstride=1, cstride=1, linewidth=0)
# Plot the central plane z = 0 as a grid
ax.plot_wireframe(X, Y, np.zeros_like(X), color='black', alpha=0.5, linewidth=0.5)
# Plot the contour lines where f(x,y,t) = 0 on the plane
ax.contour(X, Y, Z, levels=[0], colors='red', linewidths=2, linestyles='solid', zdir='z', offset=0)
# Set consistent axis limits and ensure z-axis is to scale
ax.set_xlim(-1, 1)
ax.set_ylim(-1, 1)
ax.set_zlim(-0.5, 0.5) # Z-axis scaled properly
# Set axis labels
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
ax.set_zlabel('Z axis')
# Set equal scaling for axes
ax.set_box_aspect([1, 1, 0.5]) # Aspect ratio of x:y:z
# Return the surface to update the animation
return surface
# Create the 3D animation for one full cycle (0 to 2π)
t_values = np.linspace(0, 2 * np.pi, 100) # 100 frames for one cycle
ani_3d = animation.FuncAnimation(fig, update_surface, frames=t_values, interval=1, blit=False)
# Show the animation
plt.show()
output_path = 'animation.gif'
writer = animation.PillowWriter(fps=50)
print("Saving...")
ani_3d.save(output_path, writer=writer)
# Print the output path to confirm where it is saved
print(f"Animation saved to: {output_path}")
References
- Linqi. Shao. Modeling a square vibrating plate. University of Waterloo, pages 1-58, 2018.
- Lord Rayleigh. Vibration of Plates, pages 352-361. Princeton University Press, New York, NY, 1894.
- John M. Davis. Introduction to Applied Partial Differential Equation. W. H. Freeman, New York City, 1st edition, 2012.
- Richard. Haberman. Applied Partial Differential Equations with Fourier Series and Boundary Value Problems. Pearson, New York City, 5th edition, 2018.
- Rudolph. Szilard. Theories and Applications of Plates Analysis. John Wiley and Sons, Inc., 111 River Street Hoboken, NJ 07030-5774, 2004
- Dispersion of flexural waves. https://www.acs.psu.edu/drussell/Demos/Dispersion/Flexural.html, 2004. Accessed: 2024-05-26
- Thomas D. Rossing and Neville H. Fletcher. Two-Dimensional Systems: Membranes and Plates, pages 65-94. Springer New York, New York, 2004.
- Dzmitry Bahdanau, Kyunghyun Cho, and Yoshua Bengio. Stable and accurate numerical methods for generalized kirchhoff-love plates, 2020. Accessed: 2024-05-26.
More Articles