12.9 C
New York
Monday, November 10, 2025

Crafting Generative CSS Worlds | Codrops



There’s one thing about isometric projections that evokes a comfy, nostalgic feeling. Most certainly, the perpetrator is the wave of ’90s pixel-art basic video games that etched the aesthetic into our collective reminiscence, from Populous to Transport Tycoon.

On this article, we’ll discover how one can recreate that very same attraction with trendy CSS. Extra particularly, we’ll look below the hood of the newly launched Layoutit Terrain Generator to learn the way stacked grids and 3D transforms may be mixed to create a completely addressable 3D area within the browser.

(If you wish to dive deeper into how the grid 3D construction works below the hood, the CSS Voxel Editor article explores it intimately.)

Behold! A 3D terrain constructed solely with stacked grids and reworked HTML components: no canvas, no WebGL, simply CSS doing its magic.


Free GSAP 3 Express Course


Study trendy net animation utilizing GSAP 3 with 34 hands-on video classes and sensible initiatives — excellent for all talent ranges.


Test it out

Setting the scene

After wrapping up the CSS Voxel Editor, I needed a brand new problem, one thing that pushed the bounds of the stacked grid approach. That’s how I landed on a terrain generator, particularly as a result of it means increasing the form grammar: to make it attainable, we’ll must construct angles and slopes. However earlier than all that may occur, now we have to set the stage correctly.

The .scene ingredient acts as our digital camera mount: it’s the place depth begins with the perspective property. By assigning a beneficiant worth (of 8000px) we get an almost-isometric look with a slight, pure distortion. Each youngster of this father or mother container inherits transform-style: preserve-3d, which mainly ensures that the 3D transforms work as anticipated.

The .ground ingredient defines the world’s tilt. By making use of remodel: rotateX(65deg) rotate(45deg), we angle the whole area into view, establishing the digital camera’s orientation. On prime of this base, a number of .z components are stacked vertically with translateZ(25px * stage). That approach, every layer acts as a grid slice at a selected top (a singular Z stage) whereas the rows and columns outline the X and Y coordinates. 

Inspecting the stacked grids in devtools highlights the coordinate system that powers our 3D format.

Collectively, these components create the 3D grid the place we’ll place our shapes. From this basis, our terrain can begin to rise!

<div class="scene">
  <div class="ground">
    <div class="z" model="remodel: translateZ(0px);"></div>
    <div class="z" model="remodel: translateZ(25px);"></div>
    <div class="z" model="remodel: translateZ(50px);"></div>
    <div class="z" model="remodel: translateZ(75px);"></div>
  </div>
</div>
.scene { perspective: 8000px; }

.scene * { transform-style: preserve-3d; }

.ground { remodel: rotateX(65deg) rotate(45deg); }

.z {
  show: grid;
  grid-template-columns: repeat(32, 50px);
  grid-template-rows: repeat(32, 50px);
}

Increasing the form grammar

Past easy cubes, our world requires new primitives: we name them flats, ramps, wedges, and spikes, and they’re the minimal items of our terrain technology.

Every form tilts one or two planes to outline its type. They observe a 2:1 dimetric system, the place each unit of top equals two items of depth. In observe, this ends in cells measuring 50×50×25px. The widespread face tilt of arctan(0.5) ≈ 26.565° retains geometry constant throughout tiles, making certain clear shading transitions and seamless slopes between neighboring cells.

Let’s take a more in-depth take a look at how every form comes collectively:

Flat form

Flat stays horizontal; it’s only a aircraft translated within the Z dimension by 25px, and rotated to match its cardinal orientation.

.tile.flat {
  remodel: translateZ(25px) rotate(0deg);
}

Ramp form

Ramp reuses the identical flat container, however provides one rectangular face pseudo ingredient tilted 26.565° to create the slope.

.tile.ramp {
  remodel: translateZ(25px) rotate(0deg);
}

.tile.ramp::earlier than {
  content material: "";
  place: absolute;
  inset: 0;
  transform-origin: prime left;
  remodel: rotateY(26.565deg);
}

Wedge form

Wedge combines the ramp’s sloped face with a mirrored one turned 90 levels, making a concave junction between them.

.tile.wedge {
  remodel: translateZ(25px) rotate(0deg);
}

.tile.wedge::earlier than,
.tile.wedge::after {
  content material: "";
  place: absolute;
  inset: 0;
  transform-origin: prime left;
}

.tile.wedge::earlier than {
  remodel: rotateY(26.565deg);
}

.tile.wedge::after {
  remodel: rotate(-90deg) scaleX(-1) rotateY(26.565deg);
}

Spike form

Spike mirrors the ramp to type a peak. It combines two opposing slopes: the entrance ramp leans inward, and a mirrored one rises till they meet in a convex ridge.

.tile.spike {
  remodel: translateZ(25px) rotate(0deg);
}
.tile.spike::earlier than,
.tile.spike::after {
  content material: "";
  place: absolute;
  inset: 0;
  transform-origin: prime left;
}

.tile.spike::earlier than {
  remodel: rotateY(26.565deg);
  transform-origin: backside left;
}

.tile.spike::after {
  remodel: translateZ(-25px) rotateX(26.565deg);
}

Textures and lighting

Since our shapes are simply regular DOM components, we will model them simply with CSS. On this case, utilizing background-image or background-color is your best option, as that doesn’t add new nodes (like <img> or <svg> would). As a center floor, including inline SVGs to pick out shapes could make sense when animations or interactions are wanted.

Lighting on this engine is directional and baked into the textures. We repair a lightweight supply to the west (180°) and classify every seen face into one among 4 brightness bands based mostly on its angle to that gentle. Every form receives a light-level class (.l-1 to .l-4) based mostly on its orientation to the sunshine supply. The result’s plausible shading that continues to be constant even because the scene rotates.

A better take a look at Layoutit Terra’s sprites for various shapes, biomes and lighting ranges.

Making some noise

A terrain is a heightmap: a set of 2D arrays of elevation values constructed from noise and formed right into a tough landmass. The preliminary uncooked discipline comes from a library like simplex-noise, adopted by many refinement passes. This smooths out speckles, terraces steep areas, and limits how a lot steepness can range. One of many golden guidelines of this world is that tiles can’t differ by a couple of top stage, which retains slopes constant and prevents cliffs from forming.

On the consumer facet, two most important knobs are uncovered: landmass protection, which controls the proportion of water that fills the map, and terrain sort, which units the ceiling for elevation within the scene.

A uncooked take a look at the heightmap array, the place dots mark water and numbers land elevation.

As soon as the heightmap is constructed, a classifier decides which form suits every cell. Tiles can have as much as eight attainable neighbors, every with 4 rotation states, which shortly provides as much as lots of of combos. To deal with all that complexity, a rulebook defines how shapes ought to meet at each cardinal level. And when these guidelines nonetheless fall quick (like on sharp intersections or excessive slopes) a set of manually curated overrides steps in to scrub issues up and maintain the terrain steady.

Efficiency notes 

One of many most important bottlenecks with stacked grids is what number of DOM components they’ll maintain. Each tile, face, and layer provides up, and by the point we render a big terrain, the browser is already juggling 1000’s of nodes. A 32×32×12 grid is roughly the protected restrict for many trendy programs; past that, rendering turns into unpredictable, body charges drop, and tiles could flicker or disappear altogether.

The Rendering panel in DevTools can reveal layer borders, paint flashing and body rendering stats, a useful toolkit for engaged on 3D CSS scenes.

The actual ache level got here from utilizing clip-path to attract the triangular faces for wedges and spikes. It seemed clear and purely CSS, however it pressured the browser to repaint each time the scene rotated, dragging efficiency down. The repair was to modify to pre-cut PNG sprites with clear backgrounds. Till browsers correctly optimize clip-path in 3D contexts, sprites stay essentially the most dependable selection.

Subsequent steps

Past being an incredible technical problem, this mission proved that the stacked grid approach can go far past cubes. Including slopes and angles opens up a brand new sort of depth: 3D volumes that really really feel formed by gentle and type, regardless that every little thing remains to be simply CSS.

Swapping a single class on the .scene container immediately adjustments the biome, updating the background-image textures throughout each voxel form.

From right here, there are many paths to discover. Isometric net video games are an apparent one, but in addition light-weight, interactive experiences that stay proper within the browser. The objective isn’t to switch WebGL, however to discover a unique approach of constructing 3D initiatives that keep easy, readable, and inspectable.

As for my subsequent 3D grid mission, it may contain turning the terrain inside out: mirroring two vertical grids, utilizing duplicate heightmaps to type a single steady quantity. Possibly that’s how we attain a real CSS sphere.



Supply hyperlink

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles