String Vibration Physics with React
For game developers when applying for a new job there is a common practice with testing on graphics interaction using laws of a physics. To get high-performance result with JavaScript, it is best to use WebGL and WebAssembly technologies. But we will go the other way, will implement the physics of string vibration using React.
🤓 Before diving in JavaScript implementation, we will look at the theory of harmonic oscillations.
📚 Main terms.
Oscillation is the repetitive variation of some measure about an equilibrium point.
The maximum offset from the equilibrium point is known as the amplitude(A). The time for one oscillation is called the period.
A frequency of oscillation is the number of oscillations per second and is measured in hertz(Hz). In case of the string object, the frequency depends on length of string, tension, material density, and cross-sectional area. In physics is used a term called angular frequency:
ω = 2𝜋 / 𝛵 ;
Where:
ω
- an angular frequency;- 𝜋 - a mathematical constant;
- 𝛵 - the period;
And the main thing for our application, with this all in mind, is the displacement(x) of the harmonic oscillation at the moment of time(t) is determined by the sinusoidal(sin/cos) equation:
x = A cos(ωt + φ0) ;
φ0 - is an initial phase and in this case, it can be omitted (φ0 = 0).
And the working equation for calculating the displacement will be:
x = A cos(ωt) ;
👌 The Next important point.
In nature, the energy of free oscillations gradually is decreasing over time and thus the amplitude also is decreasing:
A = Ao exp(-σt);
Ao - initial amplitude
t - time from moment oscillation is started
σ - damping of the harmonic oscillation which determines for how many cycles the amplitude decreases by 2.718(base of natural logarithm) times.
😅 Enough with the theory, let’s proceed to the practical part.
✍️ Static types
For more confidence will write the application with TypeScript and define the main types.
👨💼 Constants for our goal.
Declare some reusable values as constants.
As noticeable from titles, most of the constants are helpers for drawing and animation. More significant are:
CIRCULAR_FREQUENCY
-4
full oscillations per second, 2𝜋 just converts it to circular meaning;FREQUENCY_DAMPING
means that oscillation will be dumped in50
full cycles;
👨🎨 SVG element of the string.
Write the string element as React.FunctionalComponent
.
Using TypeScript, we strictly set number of elements in points
list.
p0, p2
are actually static node points and p1 is the point of displacement. For image smoothness, a pattern of bezier curve and q.y
(y coordinate of middle node point) is recalculated according to quadratic bezier curve expression, assuming that t
parameter always is 0.5
:
B(t) = (1 - t)² P0 + 2t (1 - t) P1 + t² P2 ;
Assuming thatP1
in our case is q.y
then will get:
q.y = (p1.y - (1 — t)² p0.y - t² p2.y) / (2t (1 — t)) ;
🛠 Util functions for proper program execution.
Below will be methods which provide proper user interaction and vibration recalculations.
isDraggableRectangle
- checks whether the user interacts inside drag areagetSafePoint
- during dragging, return coordinates restricted by draggable areagetNextPosition
- recalculates displacement according to the angular frequency expression;getNextAmplitude
- updates actual amplitude according to the frequency damping;
🎣 Animation executor in React hook style.
The animation will be played using a familiar requestAnimationFrame
method. The first time it is called synchronously after React component is mounted with the useLayoutEffect
hook. For better performance, callback
will be updated via mutable useRef
hook.
Once application layout is drawn it will call callback argument for each frame.
👷♂️ Gather all things in the main component.
Define style for application container:
Start StringPhysics
component declaring state hooks:
Animation callback with condition whether the string is free or under the tension(isDraggable
):
Add controls which will be responsible for user interaction:
Event handlers reuse controls for both mouse and touch events:
Put all needed stuff to div
element:
Last step, add StringPhysics
to the DOM
:
🖼 And try out the final result:
👨🎓 Conclusion.
Of course, it is the simplest application of programming animation using laws of physics, but everything big starts small. You can play around with this example and then see whether such an approach without WebGL could be a solution for your web games or for some visual effects on your web pages.