Back to the post
Posts

Transcript

Making stylized grass

Synthetic voices · A dialogue adaptation of this article, not a verbatim reading.

Narrator & Maya · 11:05 · Read the article

01Art direction first

Narrator: I'm Khalil. This is Maya. Today we're building the stylized grass on this site. The starting point is art direction: soft shapes, broad colors, something closer to a painted illustration. Most of the engineering is about keeping that look responsive.

Maya: So the goal isn't to model a botanically convincing leaf. It's to decide what the field should feel like, then spend the rendering budget where that matters.

Narrator: Exactly. One reference was Eric Wohllaib's GDC talk about procedural grass in Ghost of Tsushima. Large fields, individual blades, and a real performance budget. Our meadow aims for a different look, but that relationship between the blade and the whole field is useful.

Maya: Yours feels flatter and softer. More watercolor than sharp little spikes.

Narrator: That's deliberate. Rounded profiles, overlapping color, and restrained outlines. Every demo uses the real WebGL renderer. We'll change one decision at a time, so we can actually see what it does.

This part of the article

02Build one blade

Narrator: Start with a ribbon. Two points at the root, two farther up, and more pairs toward the tip. Connect neighboring rows with two triangles. That's our mesh: points and the connections between them.

Maya: And where does the work happen? Are we moving each leaf around in JavaScript?

Narrator: JavaScript builds the strip and plants the roots. The GPU runs a vertex shader to position the points while drawing. Each blade gets its own height, width and other attributes, but shares the little strip. We don't update thousands of leaf objects on the CPU every frame.

Maya: The tip control makes more sense when width stays fixed. It changes where the blade starts narrowing, rather than just making everything wider.

Narrator: Right. A larger shape exponent keeps width higher up the blade. The final point still closes, but the outline below it is fuller. Those rounded tops are how we get the fluffy look. Sharper tips create thinner, wirier marks. Try both before changing height.

This part of the article

03Bend without moving the root

Narrator: Now bend the ribbon. If we shift every point sideways equally, the whole thing slides out of the soil. Instead, the displacement grows with the square of our progress from root to tip.

Maya: Zero at the root, a quarter halfway up, and the full push at the tip. That's a curve I can picture.

Narrator: Yes. The root stays anchored while the rest bends away. We also lower the tip and limit the combined push, so a strong gust doesn't stretch the blade into something completely different.

Maya: Conveniently, no plant physics degree required.

Narrator: It's an approximation chosen for the picture. Lean, wind and touch can all feed the same bend. In the demo, watch the root while you move Lean. It should stay in place.

This part of the article

04How many triangles does a leaf need?

Narrator: How much geometry does that bend need? Each segment of the ribbon costs two triangles. With two hundred thousand blades, three segments mean one point two million triangles. Ten segments mean four million.

Maya: A small choice per blade turns into a fairly large invoice.

Narrator: Exactly. Switch Show triangles on and compare the three detail levels. Extra rows make the curve and rounded tip smoother. We place rows closer together near the tip because the shape changes faster there.

Maya: And zoom back out before deciding. A corner you can see in a close-up might disappear at the actual viewing size.

Narrator: That's the tradeoff. Fewer vertices mean less vertex work and fewer triangles to set up. But the fragment shader still colors the covered pixels. If lots of leaves overlap, that cost remains. Geometry count is one part of performance, not the whole answer.

This part of the article

05Build a clump

Narrator: A field of identical, equally spaced blades looks like a grid. We start with a clump center, scatter roots around it, and connect their height and lean to their position. That gives the group a silhouette.

Maya: There's a reason for the shorter edges too. A worn path can have sparse, disturbed growth beside fuller patches. Although nature doesn't follow that rule everywhere.

Narrator: Exactly. We're using it as a visual reference. Interior depth helps control height and spread. Small blades soften the rim, larger groups fill the interior, and occasional tall exceptions break the pattern.

Maya: This clump can go all the way to eight hundred blades. Is that the normal size?

Narrator: No, that's an expanded budget for the experiment. The usual clumps are much smaller. Increase the count, then change Roundness while keeping width fixed. Full tips overlap into a softer mass. Pointed ones read as separate strokes. Root shading helps you see the depth between them.

This part of the article

06Randomness with a structure

Narrator: Random placement needs structure. A mask describes where grass can grow. Think of a grayscale map: white allows it, black excludes it, and gray gives partial coverage.

Maya: So instead of scattering everywhere and hoping, we're giving the randomness a shape to work within.

Narrator: Yes. A simple version measures distance from a patch center and reduces the chance of planting near its edge. Our renderer also stores local thickness and interior depth. Those values guide how full or small the clumps become.

Maya: Growth is another rule. Neighboring blades get related delays, so the field doesn't appear in one instant.

Narrator: And keep the seed fixed while comparing settings. Density cutoff filters stored blades by a random rank. Growth controls emergence. Width, height and roundness change their shape. Replant changes the arrangement. If we change everything at once, we won't know what helped.

This part of the article

07Use the ground as a palette

Narrator: The ground is also a color source. Each blade samples a shared field at its root. That helps the base blend into the ground instead of looking like a separate object pasted on top.

Maya: Try Ground only first. Then sample the center and change a palette color. When you turn both layers on, you can see where the blades get their colors.

Narrator: Set root shading to zero for that comparison. Then bring it back to see how darker roots create depth inside the clump.

Maya: The texture carries height too, which isn't obvious if you only think of a texture as a photograph.

Narrator: It's a grid of values. Red, green and blue store color; alpha stores ground height. We bake that shared result and refresh it when the inputs change. Drawing uses filtered samples between texels. The readout shows the nearest texel, one cell in that grid.

This part of the article

08Color in patches

Narrator: For color, work at two scales. Broad patches give neighboring blades something in common. A smaller variation prevents the patch from becoming perfectly uniform.

Maya: If every leaf gets an unrelated color, you lose the large shape. You start looking at noise instead of a field.

Narrator: Exactly. The palette includes pale greens, teal shadows, rusty accents and muted cool areas. It's an artistic choice, not an attempt to find the one correct grass color. Keep the seed fixed and compare the patch strength.

Maya: And baking that field is part of the performance work?

Narrator: Yes. The blades sample a shared result instead of rebuilding the entire color and height calculation at every vertex each frame. Sampling still costs something. But it reuses work that doesn't need to change constantly. Wind and interaction remain dynamic.

This part of the article

09Move the field together

Narrator: Wind needs continuity. Imagine sliding a sheet of cloudy values across the ground. Nearby roots sample similar parts of it, so a gust moves through the field.

Maya: Otherwise each blade would twitch independently. You'd see the randomness before you saw the wind.

Narrator: We combine several scales of smooth noise and add a small, faster flutter. Noise here means changing numbers, not sound. Broad motion gives direction; the finer movement stops the whole field moving like one rigid object.

Maya: Scrubbing time is helpful. You can hold the same moment while comparing wind strength and gust strength.

Narrator: The demos start paused and stop rendering offscreen. Reduced-motion settings keep automatic playback still, with manual scrubbing available. Sometimes the cheapest frame is the one we don't need to draw.

This part of the article

10Push the grass aside

Narrator: For touch, convert the pointer position to a point on the ground. Send that point and a radius to the shader. Each blade checks its root's distance and bends away if it's close enough.

Maya: So we aren't checking the mouse against a few million triangles.

Narrator: No. It's a simple distance calculation. Smoothstep gives the push a soft boundary. A hard cutoff would produce a visible ring where blades suddenly switch from standing to bent.

Maya: And the tiny minimum under the division prevents trouble when the pointer lands exactly on a root.

Narrator: Right. Try the position slider if you're using a keyboard. This demo shows the current push. The homepage also remembers eight fading positions for a short wake. A few shared inputs give us that response without a separate physics simulation for every plant.

This part of the article

11Share the geometry

Narrator: Instancing ties this together. One shared mesh, plus a record for every blade. The record stores its root, height, width, lean and variation. The GPU combines those values with the strip when it draws.

Maya: That removes the need for a separate scene object and individual submission for every leaf. But the leaves themselves still need drawing.

Narrator: Exactly. This control changes the actual submitted instance count, up to two hundred thousand. Density cutoff works differently: the records stay submitted and the shader filters blades. The counter tells you how many records we're drawing, not how fast your device will be.

Maya: Two hundred thousand is an invitation to experiment, not a recommended minimum.

Narrator: Quite. Watch detail and overlap as well as count. We also turn the ribbons toward the camera so they don't disappear edge-on. That's billboarding. An orthographic camera keeps distance from shrinking the scene, which supports the illustrated, slightly two-dimensional look.

This part of the article

12Put the field together

Narrator: The final demo combines all of it. Start with thirty-five thousand blades, compare width and roundness, then try color, terrain and wind. You can increase the count to two hundred thousand, but give each change a chance to show you something.

Maya: If I were building this from scratch, I'd want one step working before adding the next. Ribbon, anchored bend, clump, placement, color, motion, interaction.

Narrator: That's the order. Decide which shapes matter before spending more geometry on them. Art direction tells you what to preserve. Engineering keeps those choices practical at the scale of a field.

Maya: And inspect the gaps. A shorter edge or a clearer empty space might help more than another thousand blades.

Narrator: I plan to open-source this renderer. The follow links below are where I'll share the release and future experiments. For now, take the demos apart a little. Keep the changes that help the picture, and pay attention to what they cost.

This part of the article

13Until next time

Narrator: Thanks for listening. Follow along for more articles, experiments, and things that worked on my machine.

Maya: Some of them might even work on yours.

Narrator: No promises. Have a lovely day. Go make something.

Maya: Preferably a cup of tea first. Bye!