Making the Ocean Move
I finally cracked animated ocean water, and I'm honestly a little giddy about it. The Coastal Cliffs used to have a flat sheet of blue tiles sitting there doing nothing. Now the water shimmers, caustics crawl across the shallows, foam laps at the shoreline, and the whole thing shifts from bright turquoise near the beach to deep blue out past the rocks. See for yourself:
The Problem With Tilemap Water
Almost every water shader tutorial starts the same way: wiggle the UVs with some noise so the texture ripples. That works great on a single big sprite. It falls apart completely on a tilemap, because all the tiles live together in one texture atlas. The moment you displace UVs, each water tile starts sampling its neighbours in the atlas, and suddenly there are chunks of cliff and sand smearing through your ocean.
My water is painted as tiles, and I wanted to keep it that way — painting water in the tilemap editor is fast and flexible. So the shader had one hard rule: it can change brightness and colour, but it can never move a pixel.
Layers, Not Tricks
It turns out you don't need distortion at all. The final effect is a stack of simple layers, each one cheap on its own:
- Depth gradient — the water blends from a shallow turquoise to a deep navy, snapped to a handful of discrete colour bands so it reads as pixel art instead of a smooth airbrushed fade.
- Caustics — two copies of a hand-authored caustic texture drift across the surface at different scales and headings, and the shader only keeps where they overlap. Because the overlap is always changing, the pattern morphs organically and never visibly repeats.
- Shoreline foam — an animated band of broken white hugging wherever water meets land, pulsing like a gentle tide.
- Sun sparkles — tiny glints where drifting sine waves cross each other, layered faintly over everything else.
Everything is computed in world coordinates and quantized to chunky 2-pixel blocks, so the animation stays crisp and blocky instead of anti-aliasing itself into soup. That one detail did more for the pixel-art feel than anything else.
The Depth Map Was the Breakthrough
The piece that took the longest to figure out was making the depth gradient follow the actual coastline. A simple top-to-bottom fade looks fine until your shore curves, and mine curves a lot.
The solution: when the scene loads, a script reads the water tiles and runs a flood fill outward from the shore, building a little texture where each pixel stores how far it is from land. The shader samples that map to decide how deep (and how dark) each spot of water should be, and the foam knows exactly where the shoreline is because that's just where the depth hits zero.
The best part is that it's all generated from the tile data at load time. If I repaint the coastline tomorrow, the depth map, the gradient, and the foam all just follow along. No code changes, no hand-authored masks.
What's Next
The Coastal Cliffs are close to their end-of-content pass now, and this water pushes the whole scene up a level. The same shader is ready to drop onto any future lake, river, or tide pool — it's all driven by tile data, so new locations get living water for free.
Thanks for following the journey. More updates soon.