Waves are ubiquitous, being found nearly everywhere we look in nature. There are many kinds: mechanical (waves in water, sound, vibrating strings), electromagnetic (light is wave!), and the wavelike nature of all particles according to quantum mechanics. Despite this variety, just a few mathematical equations can describe a great deal of wave phenomena. If you have a solid understanding of the sine function, you're off to a good start. Most of the trickiness is in notation, which I'll try to clear up here. If you start to get bored, there's a bit of music theory at the end.
A general wave definition is something like "a propagating disturbance". For example, think of the ripples when you throw a rock into a pond. Waves are typically described by the thing that's changing (the height of water) and the medium in which the wave exists/moves (the pond water). Waves can be both spatial and temporal. That is, the ripple pattern exists over some distance or region of the pond, and it is also changing in time. Don't think of the wave as water physically moving from one place to another within the pond. Rather, we are describing the height of water in the pond using a function of location and time—and the function happens to be a wave.
Other wave examples (variable / medium):
Image of wave from wikibooks
Above is a picture of a typical sine wave. Not all waves are perfectly sinusoidal, but these are convenient to analyze. (Others include square, triangle, and sawtooth waves.)
Using the pond example again, the height of water is called the displacement and is measured with respect to some equilibrium value—what the height would be with still water and no disturbance. The displacement can take on both positive and negative values, oscillating about this equilibrium value (which is almost always chosen to be 0). The distance between two similar points on the wave shape (e.g. peak to peak, shown in the image) is called the wavelength, λ.
Let's say we want to actually write down a sine function to describe the ripple pattern in the water (we're just taking a snapshot in time right now). Technically, we're also going to limit ourself to one dependent variable x, even though pond surface exists in 2 dimensions. We might start with something like:
height=y=sinxWe know the sine function repeats itself every 2π units, and we said the distance between two similar points is λ. So, when x changes by λ, we want our function to return the same value. Let's try this:
y=sin(2πxλ)Check that this function works, and also note that dimensional analysis requires something of this form. Special functions like sine, cosine, exponential, etc. require a dimensionless expression within the function. Our dependent variable x has dimensions of length, so we must divide by something else with dimensions of length (λ).
Finally, we might want to compare two waves with the same wavelength, but one is shifted relative to another. Lucky for us, the sine function slides to the left/right when you add positive/negative values to the function's expression. We call this the phase shift ϕ:
y=sin(2πxλ+ϕ)In other words, we might not always want y=0 at x=0, so we can choose the appropriate location for y=0 by adjusting ϕ.
import plotly.graph_objs as go
from ipywidgets import interactive, HBox, VBox, widgets, interact
import numpy as np
xs = np.linspace(0, 5, 1000)
def wave(lam, phi, x):
return np.sin(2*np.pi*x/lam + phi)
fig = go.FigureWidget()
fig.layout = dict(yaxis=dict(range=[-1,1]), xaxis=dict(range=[0,5]),width = 1000)
fig.update_layout(
title={
'text': r'$y = \sin \Big(\frac{2 \pi x}{\lambda} + \phi \Big)$',
'y':0.85,
'x':0.5,
'xanchor': 'center',
'yanchor': 'top'})
fig.update_xaxes(
title_text = r"$x$",
title_standoff = 15,
ticks = 'outside',
ticklen = 10,
tickcolor='white')
fig.update_yaxes(
title_text = '',
title_standoff = 25,
ticks = 'outside',
ticklen = 10,
tickcolor = 'white')
line = fig.add_trace(go.Scatter(x=xs, y=wave(1, 0, xs),
mode='lines',
name='lines'))
slider = widgets.FloatSlider(
value = 1,
min=0.1,
max=10,
step=0.1,
readout=True,
description=r'$\lambda$')
slider.layout.width = '400px'
slider2 = widgets.FloatSlider(
value = 0,
min=-3.14,
max=3.14,
step=0.01,
readout=True,
description=r'$\phi$')
slider2.layout.width = '400px'
def update_range(lam, phi):
fig.data[0].y=wave(lam, phi, xs)
vb = VBox((fig, interactive(update_range, lam = slider, phi = slider2)))
vb.layout.align_items = 'center'
vb
VBox(children=(FigureWidget({ 'data': [{'mode': 'lines', 'name': 'lines', 'typ…
What if we want to also describe how the ripples in the ponds change over time? First, we should consider a duck floating at a specific point on the pond and riding the waves up and down. The height of the duck is a function of time, and notice that it oscillates up and down like a wave. In fact, we can describe the duck's displacement using the sine function again, but with time t as the dependent variable:
duck height=y=sin(2πft).
The f in the sine expression is called the frequency of the wave. It is the number of oscillations per second. In the duck animation below, it takes about two seconds from peak to peak (2 seconds each oscillation). That means half an oscillation each second, or f=0.5 Hz. The units, Hertz (Hz), is another way of saying "per second" or Hz = 1s.
Duck animation from brilliant.org
Now, if we want to forget about the duck and describe the water waves as both a function of space and time, we can combine our spatial and temporal sine functions. Remember how a negative phase shift ϕ would move the entire wave shape to the right? What if we use that property of the sine function to our advantage and write:
y=sin(2πxλ−2πft).
Within this sine function, the left term describes the wave shape over space. As time passes, t increases, and the this wave shape slides to the right with some speed. We call this a traveling wave. Most textbooks/physicists will not write this function in terms of wavelength and frequency. Instead, the wavenumber k and angular frequency ω are defined:
k=2πλAngular frequency is almost like frequency, except it is measuring "radians per second" rather than "oscillations per second", with an implicit understanding that each oscillation corresponds to 2π radians. Wavenumber is almost like a spatial frequency, which measures "oscillations per unit of distance". In physics, the factor of 2π is also included in the definition of wavenumber, so k measures "radians per unit of distance".
It can take some time to get used to the notation, so it's good to keep these relations handy while it all sinks in. One more useful thing to remember: the traveling wave moves at a velocity v, where
v=ωk.
You can see how wavenumber and angular frequency change a wave in the animation below.
xs2 = np.linspace(0, 5, 500)
def traveling_wave(k, omega, x, t):
return np.sin(k*x - omega*t/10.)
fig2 = go.FigureWidget()
fig2.layout = dict(yaxis=dict(range=[-1,1]), xaxis=dict(range=[0,5]),width = 1000)
fig2.update_layout(
title={
'text': r'$y = \sin(kx - \omega t)$',
'y':0.85,
'x':0.5,
'xanchor': 'center',
'yanchor': 'top'})
fig2.update_xaxes(
title_text = r"$x$",
title_standoff = 15,
ticks = 'outside',
ticklen = 10,
tickcolor='white')
fig2.update_yaxes(
title_text = '',
title_standoff = 25,
ticks = 'outside',
ticklen = 10,
tickcolor = 'white')
line2 = fig2.add_trace(go.Scatter(x=xs2, y=traveling_wave(1, 0, xs2, 0),
mode='lines',
name='lines'))
bslider = widgets.FloatSlider(
value = 3.14,
min=0.1,
max=10,
step=0.02,
readout=True,
description=r'$k$')
bslider.layout.width = '400px'
bslider2 = widgets.FloatSlider(
value = 0,
min=-1,
max=1,
step=0.1,
readout=True,
description=r'$\omega$')
bslider2.layout.width = '400px'
play = widgets.Play(
interval = 5,
value = 0,
min=0,
max=500,
step=1
)
def update2(k, omega, time):
fig2.data[0].y=traveling_wave(k, omega, xs2, time)
vb2 = VBox((fig2, interactive(update2, k = bslider, omega = bslider2, time = play)))
vb2.layout.align_items = 'center'
vb2
VBox(children=(FigureWidget({ 'data': [{'mode': 'lines', 'name': 'lines', 'typ…
Sound is also a wave, but a bit different than the water wave example we've been using. Rather than the height of water oscillating up and down, the air pressure fluctuates. On average, the air pressure is constant throughout a room (atmospheric pressure), but it can change locally. When you hit a drum, the drum head oscillates up and down, which in turn compresses air molecules near the head's surface in a cyclical fashion. This compression propagates as a high pressure wave front, and these repeated compression/expansion (high/low pressure) cycles are perceived as sound. This is an example of a longitudinal wave.
Speaker animation from NPR
When you listen to music, usually a speaker creates the sound by vibrating a speaker head (a thin membrane). The sound waves propagate through the air in the room and cause your ear drum to vibrate with the same pattern—delayed by however long it takes the waves to travel from the speaker to your ear. And to be clear, most sounds aren't just a single sinusoidal wave with one specific frequency. There's usually a mess of many different frequencies, each with their own amplitude and phase, along with other structure in the wave form (not everything that makes a noise is a simple harmonic oscillator).
It would take an entire notebook to describe how standing waves and harmonics lead to the different sounds of instruments/voices. We could also spend ages discussing how the inner ear and subsequent neural circuitry allows you to interpret sound. For now, let's just play with your ear/brain's perception of two overlapping frequencies. I apologize if you cannot hear the following, and I've included a plot of the audio wave as well.
from IPython.display import Audio,display, clear_output
xs3 = np.linspace(0, 0.2, 1000)
f1 = widgets.IntSlider(value = 1, min = 1, max = 600, step = 1, description = "frequency 1")
f1.layout.width = '400px'
f2 = widgets.IntSlider(value = 1, min = 1, max = 600, step = 1, description = "frequency 2")
f2.layout.width = '400px'
button = widgets.Button(description='Play Sound')
sound_out = widgets.Output()
fig3 = go.FigureWidget()
fig3.add_trace(go.Scatter(x=xs3, y=np.sin(2*np.pi*1*xs3) + np.sin(2*np.pi*1*xs),
mode='lines',
name='sum'))
fig3.add_trace(go.Scatter(x = xs3, y = np.sin(2*np.pi*80*xs3), mode = "lines", opacity = 0.2, name = "frequency 1"))
fig3.add_trace(go.Scatter(x = xs3, y = np.sin(2*np.pi*80*xs3), mode = "lines", opacity = 0.2, name = "frequency 2"))
fig3.layout = dict(yaxis=dict(range=[-2,2]), xaxis=dict(range=[0,0.2]),width = 900)
fig3.update_layout(
title={
'text': r'$y = \sin(2 \pi f_1 t) + \sin(2 \pi f_2 t)$',
'y':0.85,
'x':0.5,
'xanchor': 'center',
'yanchor': 'top'})
fig3.update_xaxes(
title_text = r"$t \text{ (seconds})$",
title_standoff = 15,
ticks = 'outside',
ticklen = 10,
tickcolor='white')
fig3.update_yaxes(
title_text = '',
title_standoff = 25,
ticks = 'outside',
ticklen = 10,
tickcolor = 'white')
fig3.update_layout(showlegend=False)
def on_button_clicked(_):
with sound_out:
framerate = 44100
sec = 2
t = np.linspace(0, sec, framerate * sec)
data = np.sin(2 * np.pi * f1.value * t) + np.sin(2 * np.pi * f2.value * t)
display(Audio(data, rate=framerate, autoplay=True))
clear_output(wait = True)
def update_trace(freq1, freq2):
fig3.data[0].y = np.sin(2*np.pi*freq1*xs3) + np.sin(2*np.pi*freq2*xs3)
fig3.data[1].y = np.sin(2*np.pi*freq1*xs3)
fig3.data[2].y = np.sin(2*np.pi*freq2*xs3)
button.on_click(on_button_clicked)
widgets.VBox([fig3,button])
vb = widgets.VBox((fig3, interactive(update_trace, freq1 = f1, freq2 = f2), button))
vb.layout.align_items = 'center'
display(vb)
display(sound_out)
--------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-1-632065ce4dac> in <module> 1 from IPython.display import Audio,display, clear_output 2 ----> 3 xs3 = np.linspace(0, 0.2, 1000) 4 5 f1 = widgets.IntSlider(value = 1, min = 1, max = 600, step = 1, description = "frequency 1") NameError: name 'np' is not defined
Some fun things to try: