Grasped logo
SandboxGames

© Copyright 2026 by Grasped

Blade of grass

Hard
simulation
animation
physics
motion
canvas
oscillation

Bring a blade of grass to life in the wind. Program its bending, inertia, and smooth oscillations using math and Canvas.

Goal

Render a single blade of grass made of connected segments and animate it over time. Wind should bend the blade smoothly, with stronger influence near the tip and weaker near the root. Your task is to correctly implement the wind function, influence distribution, and segment coordinate updates so the motion looks natural.

JavaScript

Loading...

Preview • sandboxed

Console output

How It Works

The general model for simulating grass blade behavior is as follows:
  • The grass blade itself is a line split into segments that form an arc.
  • Wind is a function that describes horizontal influence on the blade.
  • Wind distribution along the stem describes how strongly wind affects the blade depending on height.
Now let's look at each of these parameters separately.

Grass Blade

As mentioned earlier, the grass blade consists of segments. Let blade height be HHH, and number of segments be NNN. Then the height of each segment is L=HNL=\frac{H}{N}L=NH​. We also need the concept of relative segment height S=iN∈[0,1]S=\frac{i}{N} \in [0, 1]S=Ni​∈[0,1], where iii is the segment index. One more required parameter in this model is αi\alpha_{i}αi​, the angle that defines how far segment iii is tilted from its vertical position. This parameter will later be determined by wind and influence functions.

Wind

Generation

The key idea here is that wind is a function ω(t)∈[−1,1]\omega(t)\in[-1,1]ω(t)∈[−1,1] that defines how strongly the wind blows to the right or left at time ttt. This function can be defined in different ways, for example by a sine function. However, to get closer to realistic behavior, we will use a smoothed deterministic noise function.
Assume wind changes every Δt\Delta tΔt seconds. Let the time step be jjj. That means wind changes at time j⋅Δtj\cdot\Delta tj⋅Δt. For example, if Δt=0.5\Delta t=0.5Δt=0.5, then wind changes at times 0.5,1.0,1.5,2.0,...0.5, 1.0, 1.5, 2.0, ...0.5,1.0,1.5,2.0,...
Now let's see how to get wind values at each step. For this we need a generator that returns a deterministic value in the range [−1,1][-1, 1][−1,1] for each jjj. There are many possible implementations. Here is one of them:
Hash(j)=2⋅(sin(j∗127.1)⋅43758.5453−⌊sin(j∗127.1)⋅43758.5453⌋)−1Hash(j)=2 \cdot (sin(j * 127.1) \cdot 43758.5453 - \lfloor sin(j * 127.1) \cdot 43758.5453 \rfloor ) - 1Hash(j)=2⋅(sin(j∗127.1)⋅43758.5453−⌊sin(j∗127.1)⋅43758.5453⌋)−1
The coefficients 127.1127.1127.1 and 43758.545343758.545343758.5453 were chosen arbitrarily.

Smoothing

Now the wind generator is defined, but if we change wind values abruptly, the blade will instantly change its position. To avoid that, we need to smooth transitions between steps. For this we need a smoothing function. As with the generator, there are several options. We will use one of the most common ones: ease-in/ease-out interpolation.
Smooth(u)=u2⋅(3−2⋅u)Smooth(u) = u^2\cdot(3 - 2 \cdot u)Smooth(u)=u2⋅(3−2⋅u) u=t/Δt−ju=t/\Delta t - ju=t/Δt−j
Here, uuu is the normalized time offset between step j−1j-1j−1 and step jjj, so u∈[0,1]u \in [0, 1]u∈[0,1].
To keep this function between Hash(j)Hash(j)Hash(j) and Hash(j−1)Hash(j-1)Hash(j−1), we interpolate it: take Smooth(u)Smooth(u)Smooth(u) as the interpolation parameter and smoothly blend values Hash(j−1)Hash(j-1)Hash(j−1) and Hash(j)Hash(j)Hash(j).
Lerp(a,b,c)=a+(b−a)⋅cLerp(a,b,c) = a + (b - a) \cdot cLerp(a,b,c)=a+(b−a)⋅c a=Hash(j−1)a = Hash(j-1)a=Hash(j−1) b=Hash(j)b = Hash(j)b=Hash(j) c=Smooth(u)c = Smooth(u)c=Smooth(u)
So we can define the resulting smoothed deterministic function at time ttt:
F(t,Δt)=Lerp(Hash(j),Hash(j+1),Smooth(u(t)))F(t,\Delta t)=Lerp(Hash(j), Hash(j+1), Smooth(u(t)))F(t,Δt)=Lerp(Hash(j),Hash(j+1),Smooth(u(t))) u(t)=t/Δt−ju(t)=t/\Delta t - ju(t)=t/Δt−j j=⌊tΔt⌋j=\lfloor \frac{t}{\Delta t} \rfloorj=⌊Δtt​⌋

Additional Approximation

As a final step to make behavior more realistic, we combine slow and fast wind oscillations. For this, we take values F(t,Δt)F(t,\Delta t)F(t,Δt) with different Δt\Delta tΔt and corresponding coefficients. As a result, we get the wind function we need:
ω(t)=0.9⋅F(t,0.8)+0.1⋅F(t,0.15)\omega(t)=0.9 \cdot F(t,0.8) + 0.1 \cdot F(t, 0.15)ω(t)=0.9⋅F(t,0.8)+0.1⋅F(t,0.15)
Thus, F(t,0.8)F(t,0.8)F(t,0.8) gives slow smooth oscillations with higher weight, while F(t,0.15)F(t,0.15)F(t,0.15) gives faster oscillations with lower weight, adding realism.

Wind Influence Along the Stem

To make the blade behave naturally, its root should stay firmly attached to the ground, while the tip is freer and affected the most. In other words, wind should contribute differently at different heights. This introduces the function Influence(S),S∈[0,1]Influence(S), S \in [0, 1]Influence(S),S∈[0,1]. Here, S=0S=0S=0 is the root and S=1S=1S=1 is the tip. From this behavior we can conclude that the function should be increasing, but not too steep, otherwise the blade will simply be pressed to the ground. In general, there is room to experiment here. The simplest and most straightforward option is a linear increasing function:
Influence(S)=SInfluence(S)=SInfluence(S)=S
In this case, wind influence increases uniformly from root to tip. The behavior is reasonably realistic. You can also use a parabolic function:
Influence(S)=SpInfluence(S)=S^pInfluence(S)=Sp
where ppp is a coefficient chosen manually, for example p=2p=2p=2. With this function, influence near the tip becomes much stronger than near the root. You can increase this effect even more:
Influence(S)=Sp+SInfluence(S)=S^{p+S}Influence(S)=Sp+S
Overall, there is room for tuning and experimentation.

Result

After everything above, only one step remains: compute the final coordinates of each segment. For this we calculate the deviation angle. It cannot exceed 90 degrees or π2\frac{\pi}{2}2π​, so the blade does not bend below the ground. The final deviation angle for segment iii is:
αi(t)=π2⋅Influence(Si)⋅ω(t)\alpha_{i}(t) = \frac{\pi}{2} \cdot Influence(S_i) \cdot \omega(t)αi​(t)=2π​⋅Influence(Si​)⋅ω(t)
And coordinates of segment iii:
xi(t)=xi−1(t)+sin(αi(t))⋅Lx_i(t) = x_{i-1}(t) + sin(\alpha_{i}(t)) \cdot Lxi​(t)=xi−1​(t)+sin(αi​(t))⋅L yi(t)=yi−1(t)+cos(αi(t))⋅Ly_i(t) = y_{i-1}(t) + cos(\alpha_{i}(t)) \cdot Lyi​(t)=yi−1​(t)+cos(αi​(t))⋅L
At the same time, x0(t),y0(t)x_0(t), y_0(t)x0​(t),y0​(t) are always fixed, because the blade is rigidly attached to the ground.
Now that we have the formula for computing coordinates of each segment, take this challenge and implement this logic. Good luck.