i built a three.js portfolio you can walk around in.
a three.js and GLSL portfolio by Vipin Pathak aka CURLLMOOHA : a dandelion loader, two ways to look around, grass that leans, trees that look away, rain that soaks in, and a fire that knows when it’s wet.
you land here. everything in this picture is moving a little.
okay. i’m vipin, a three.js developer from india, and my portfolio is a meadow.
you land at a camp, a whole day goes by in three minutes, and instead of scrolling to my projects you walk to them. it’s react three fiber, glsl, and way too many evenings: curllmooha.com.
the pond and the screens and the sketchbook get all the attention. fair. but when someone tells me the place feels alive, they’re never talking about those. they mean the grass leaning, or the rain, or a lantern that won’t sit still.
so this post is about the small stuff.
it starts with a dandelion
even before the meadow loads, there’s a dandelion. no loading bar. no percentage. the seed head just fills in while the world loads behind it, and when it’s ripe you blow it.
left to right: waiting, blowing, landing.
here’s the part i like most. that tap is not just for show. browsers won’t let a page make a sound until you’ve done something, so the tap is also the “okay, you may play music now” gesture. so the opening doesn’t run on a timer. it waits for you. you blow, the seeds fly, and the meadow is finally allowed to make a sound. and there are no audio files in the project at all: everything you hear is synthesised live, note by note.
the seeds carry the letters of my name down onto a floating island. each seed is a proper little dandelion seed with a parachute made of fifty-six hairy bristles. i drew all of them with three instanced meshes, so ten parachutes and fifty loose seeds cost three draw calls. yes, i counted.
two ways to look at it
up at the top of the screen there’s a switch: 3rd person or 1st person. flip it whenever you like. it’s the one setting i’d never take out.
left: third person. right: first person, same spot.
third person is the default. the camera sits behind and above your character and follows the character around, so you can see the character, the camp and the place they’re standing in all at once. it’s the “look, a tiny person in a big meadow” view.
first person drops the camera to the character’s eyes. i gave it a wider field of view than you’d expect (72 degrees) on purpose, so the meadow wraps around you instead of feeling like you’re looking down a tube. you can look up at the sky too, up to about 38 degrees, because a meadow you can’t look up in is just a floor.
the camera swoops from behind the character to their eyes.
one small thing that only exists because of this switch: in first person the trees stop opening windows for you (more on that in a minute), because there’s no character in view to hide.
grass in three.js: real blades, real wind
there’s no grass texture anywhere in this project. every blade is real geometry, a thin ribbon with four segments so it can bend along its length. one chunk of meadow holds up to 2,850 of them, drawn as a single instanced mesh, and chunks stream in as you walk.
the character is standing in it, not on it.
the terrain and grass groundwork started from infinite-terrain by Misha Kiiatkin (MIT). i took it apart and made it mine.
wind that actually arrives
my first wind just swayed in place and it looked like shimmering. real wind arrives. so now there are three layers:
- a big slow lean from a scrolling noise texture, so neighbouring blades lean together
- a smaller random lean on top, so no two blades agree
- a gust: one soft band that sweeps across the field in the wind’s direction. it’s measured from where you’re standing, so you always watch it come at you
and the gust isn’t just a grass thing. it also shoves the bird feeder and the kite. it’s weather, not an effect.
watch the lean travel, not the blades.
the grass remembers you
walk through it and the blades around your path bow away from where you’ve been. tiny thing. it’s also why the grass feels like somewhere you’re in.
grass has no idea what a light is
this one cost me an afternoon. three.js lights don’t reach a custom grass shader, so at first the grass stayed lit like it was noon while the rest of the world went through the day. very funny. very wrong.
so the grass reads the world by hand. the sun, the moon, the lamps and the fire get published continuously and the shader copies them. dusk turns the blades olive. at night each blade catches a thin edge of moonlight. next to the fire they go warm.
same grass, a bit later.
the trees look away (a see-through dither in three.js)
okay, this is the one i’m secretly proudest of.
trees are the only thing in the meadow big enough to hide you from the camera. so when you walk behind a canopy, the leaves open a little window right over the character. it’s an oval, a bit taller than wide, the character’s own proportions, and it fades out into the leaves on a fine dither instead of a hard edge.
look at the edge: that’s dots, not a circle.
a few choices that make it work, and that i’d steal if i were you:
- no rim. a clean circle looks like a hole punched in the tree. dots thinning out look like leaves thinning out
- the dots are the same size on every screen, about a pixel and a half, so a phone and a 4k monitor look the same
- the edge wavers slowly, so it reads as foliage and not as a shape stamped on the screen
- the leaves lean aside a little, the way branches give when you walk under them. only a little, though. push them further and they pile up into a ring, which looks like a tunnel
- only trees do it, and only the part that’s in front of you. nothing behind you is touched. and it switches off in first person, where you don’t need it
the same dither frays the edge of the meadow where the world ends, so the soil breaks into fine dots instead of hard scallops.
weather
there’s a switch at the top: sunny or rain. flip it.
same spot. the grass gets heavier and the sky closes in.
rain is lit by what’s around it
my first rain was one fixed pale blue. at night it drew bright white lines across a black sky, which is rain nobody could possibly see. i looked at it for a while and felt bad.
Get Vipin Pathak’s stories in your inbox
Join Medium for free to get updates from this writer.
real rain doesn’t have a colour of its own. it shows you the light around it. so now a drop picks up the sky it refracts (most of the day, almost none of the night), and it picks up the lamps and the fire near it, strongest when it’s between you and the light. that’s why rain around a street lamp sparkles.
one lamp, one rainy night.
a splash is three things
when a drop lands, it bursts into a few droplets, they get thrown up and fall back, and it leaves a ring. on soil the ring is quick and tight, about a quarter of a second. on the pond it’s slower and wider, nearly three quarters of one.
the cpu only picks where the next drop lands, a few dozen times a second. the flight, the fade and the rings are all worked out on the gpu from the moment of impact, so nothing gets rewritten per frame. up to 96 splashes are alive at once (44 on weaker devices).
the pond takes its rings slowly.
the small things
this is my favourite part. none of it’s on a checklist and nobody would miss it. but it’s why the place feels like it keeps going when you stop moving.
the ground gets soaked, slowly
wetness is not the same as rain. the meadow follows the rain slowly. it takes most of a minute of rain to soak and longer to dry, so a shower leaves something behind.
watch the ground in this one. it starts dry and light, then the soil darkens as it takes the water, and puddles start to gather where it dips.
a couple of minutes of rain, sped up.
puddles gather on level ground only, and they fill outward the longer it rains. never inside the fire ring, never on the pond’s bank. they get their own rings and little glints from the lamps.
the fire knows
in the rain the campfire doesn’t just switch off. wet fuel puts a fire out gradually, and the heat outlasts the flame.
i shot this one at night, because that’s when you can actually see it. at first the fire burns happily, throwing sparks and a thin plume of smoke. then the rain starts. the flame shrinks as the logs soak. the wet, hot wood smoulders and keeps smoking for a while after the flame is gone, then you get steam, then nothing. stop the rain and, as the wood dries, the fire should come back. i did not plan to be this invested in a fire.
sped up. watch the smoke after the flame is gone.
a lantern that won’t hold still
every lamp post has a lantern on a chain, and the model comes with its own swing animation. but that animation has no idea which way the wind blows. so the gust shoves the whole lantern downwind about its socket and a spring pulls it back. it’s pushed while the gust crosses, then swings back through rest once it’s passed. that’s why the lamp trails the grass instead of moving with it.
the lamps also light the grass around them: up to five at a time, reaching about eight and a half units. that’s why the meadow glows near each post and goes dark between them.
[[ IMAGE: 15-lamp-swing.gif ]]
pushed by the same gust as the grass, then it trails it.
the tvs have a plug
on the hill there’s a heap of old tvs. they play my projects. and they’re on because there’s a cable.
an actual cable runs across the grass from the pile to a lamp post, with a little junction box strapped to the pole. that’s it. that’s the reason the screens work. the tvs are plugged into the lamp. a boring detail. also the whole difference between a prop and a place.
left: the junction box on the lamp post, and the cable leaving it. right: the same cable going into the pile.
follow it and you can see the whole run, lamp to tvs:
the meadow is also slowly taking the pile back: ivy climbs up the back and sides. after dark the screens throw their colour onto the ground in front of them, and a few moths turn up for the light.
the board that says who made all this
walk south from the camp and you’ll find a noticeboard. it’s the one landmark in the meadow that’s about a person instead of a place. a pitched roof of terracotta shingles, cream painted planks, dark brown lettering, like a sign somebody painted by hand.
the meadow keeper’s board. ivy included.
the name on it is raised in the same font the opening drops out of the sky, so you meet it a second time in the same letters. there’s a little warm light under the eave that pools on the lettering at night, and the birds use the roof ridge as a perch.
and there’s a bamboo wind chime hanging off the corner. it sways with the same gusts as everything else, and a strong enough gust knocks the clapper into the tubes. each strike plays that tube’s note. so the weather plays music.
the sketchbook
out east, there’s a book sitting in the grass.
it’s my sketchbook: the little three.js experiments i made along the way. the cover is cognac leather, stamped with a seed head, because the whole meadow is made of seeds and the book is basically a record of them. when it appears, a puff of wind bows the grass around it.
two things i obsessed over. first, the cover: a hardcover is heavy, so mine lifts against its own weight, tips past the balance point, falls open fast and rocks once before it settles. no gliding. second, the pages: every sketch is mounted like a plate in a printed book, with a paper margin around it, never cropped. under each one, a small lowercase caption in the meadow’s own voice.
the birds are building
look up in the trees and some of them have nests. a woven pouch, a flask, a gourd, each hanging from a branch on its own little cord, just below the leaves, so you can see them without hunting.
about one tree in ten has them, and more near camp. birds nest near a feeder they can rely on, and that’s also where you hang out. every tree is the same model, so i picked the spots where a nest can hang once, from its skeleton.
and some nests are still being built. a bird that’s building spends most of its day off working on it, and the progress is remembered for your whole visit: a nest that finished while you were watching stays finished. when it gets dark they go home. when it rains they go looking for cover. nothing happens at once. each bird’s clock runs out at a different time, so in the morning they wake up one by one, like actual birds who didn’t set an alarm.
why i bother
nobody asked for any of this. a portfolio has to show the work. it does not need puddles.
but two years of three.js taught me that people don’t remember the effect. they remember the moment something reacts. the grass bowing after you. the fire giving up. a lantern still swinging after the gust has gone. the leaves opening a window because they noticed you.
so go stand in the meadow. blow the dandelion, switch it to rain, wait a minute, and watch the fire: curllmooha.com.
if you build things like this, or want something like it built, i’m easy to find on the site. and tell me: what tiny detail would you add to a place like this?
vipin pathak · creative developer







