[Open Source] Runtime Parallel Terrain Generation with Caves, Springs, and Vegetation

Hey everyone,

I’m stepping away from making Roblox games, and rather than let old projects rot in a private repo somewhere, I’m open-sourcing them.

I originally built this for a horror game I was making. A month ago, I switched to building that game in Unreal Engine instead, since Roblox hasn’t been kind to small, talented creators.

Anyway, I’ve since cleaned up the terrain gen system so it’s pretty easy to drop into your projects.

You can access the project on GitHub here!

Alternatively, if you’d like to access the uncopylocked place it can be found here.

Features

  • Chunk-based terrain loading and unloading around players
  • Parallel Luau Actor workers for terrain computation
  • Configurable fractal noise height generation
  • Smoothed terrain surfaces
  • Underground cave generation
  • Small randomly generated springs/water pools
  • Forest and plains biomes
  • Deterministic vegetation placement
  • Trees, rocks, flowers, ferns, and other vegetation
  • Optional ore and chest generation in caves
  • Deterministic generation from a shared server seed
  • Automatic creation of all runtime folders, events, and workers

Installation

All terrain code belongs inside ServerScriptService.Terrain.

The required asset folders belong directly inside ReplicatedStorage.

ServerScriptService
└── Terrain
    ├── Config
    ├── Runtime
    ├── Noise
    ├── TerrainGen
    ├── Vegetation
    ├── OreGenerator
    ├── ChunkWorker
    └── TerrainLoader

ReplicatedStorage
├── Rocks
├── Trees
└── Vegetation

ChunkWorker should stay disabled. TerrainLoader clones and enables it automatically inside the generated Actor workers.

You don’t need to create anything in Workspace. The system builds these folders at runtime:

Workspace
├── Vegetation
├── OreChunks
└── TerrainWorkers

Once the scripts and assets are in place, hit Play. Terrain generates around each player automatically.

Configuration

The main settings live in ServerScriptService.Terrain.Config.

You can configure:

  • Seed behavior
  • Chunk size and region height
  • Player view distance
  • Worker count
  • Terrain height and noise scale
  • Cave depth and frequency
  • Spring size, frequency, banks, and water depth
  • Forest and plains biome distribution
  • Tree, rock, and vegetation density
  • Ore rarity and cluster size

Setting Config.Seed to 0 picks a random seed each server. A specific number gives repeatable terrain.

Chunk size and region height need to stay multiples of the terrain cell size.

Springs and caves

These are separate features:

  • Springs are the small surface water pools scattered through the generated terrain.
  • Caves are the underground spaces carved with three-dimensional noise.

Both can be adjusted independently in Config.Spring and Config.Caves.

Optional ore generation

Ore generation stays inactive when ReplicatedStorage.Ores is absent.

To use it, add:

ReplicatedStorage
├── Ores
└── Chest

Each ore model needs a matching entry under Config.Ores.Definitions. The chest model is optional.

Public API

The main TerrainGen module exposes:

  • TerrainGen.ComputeChunk()
  • TerrainGen.ApplyChunk()
  • TerrainGen:GenerateChunk()
  • TerrainGen:UnloadChunk()
  • TerrainGen:GetNearestChunk()

So if you want to build your own streaming system instead of using the included player-based loader, you can use the generator directly through these.

Notes

  • Generated runtime folders don’t need to be included in your place.
  • Models can include an optional Offset Vector3 attribute for placement correction.
  • Obviously, very large chunks or view distances will increase generation time and memory usage.

Feedback and contributions

Bug reports, improvements, and pull requests are welcome on GitHub.

If you end up using this system in your game, I’d love to see what you make.

6 Likes

I’m afraid you’ve committed a crime…The crime of making something THIS high-quality free.


Oh shoot, wait, hang on, I think the ores might have a problem with floating.

1 Like

I’ll look into this I think I can fix it with raycasts. I swear I remember fixing this a long time ago I guess I lost the old version that fixed this

Yeah, you raycast onto the nearest normal, and I guess have it moved there. Unfortunately, it may be difficult to find the nearest FLAT set of normals, so, while it will be stuck to the ceiling of the cave, it will probably be on top of a triangular piece.

Yeah, that’s the main issue with using a single raycast normal. My idea to fix this is firing a cluster of raycasts around the placement point, finding the largest set of similar normals, then averaging all of them to get a surface plane for the ore to snap onto.

I’ll try implementing something like this later.