The fastest Roblox terrain generator is already built into Studio. Open the **Terrain Editor**, go to the **Create** tab, pick **Generate**, choose a few biomes and click Generate. If you have a real or hand-drawn map, use **Import** with a heightmap instead, and if you want procedural terrain from code, write a [Roblox terrain generator](/how-to-make/roblox-terrain-generation) script with math.noise and Terrain:WriteVoxels.
Option 1: The built-in Generate tool (no code)
The Roblox docs suggest using Generate to make a large map and then fine-tuning it with the Edit tab tools. Here is how:
- Open the Terrain Editor (Home tab, then Editor in the Terrain section), go to the Create tab and select Generate.
- Move or resize the selection region in the viewport. For exact numbers, type the X/Y/Z Size and Position under Selection Settings, and keep Snap to Voxels on.
- In Biome Settings, tick the biomes you want. The options in Studio are Arctic, Dunes, Canyons, Lavascape, Water, Mountains, Hills, Plains and Marsh.
- Set Blending (higher values are a bit smoother), Biome Size (how big each biome is inside the whole area) and Caves (turn on to include caves). Roblox doesn't document number ranges for these, so change one setting at a time and regenerate to see what it does.
- Click Generate. If you don't like the layout, change the Seed and generate again. A new seed gives new terrain with the same settings.
- Polish the result with the Edit tab tools such as Sculpt, Smooth, Paint and Flatten.
Two tips:
- Pick two or three biomes instead of all nine. Mixing all of them tends to look busy.
- Be careful with the Clear tool. It deletes all terrain in the place, including areas outside your selection.
Option 2: Import a heightmap
Use the Import tool when you already have a map image, such as a real-world elevation map or something you painted yourself. Work through it in this order:
- In the Terrain Editor's Create tab, select Import.
- Choose a heightmap image in .png or .jpg format, up to 4096x4096 pixels. Brighter pixels become higher ground.
- Set the region's Position and Size. Each pixel covers 4 studs, so size the region to match the image. A 1024x1024 px image covers 4096x4096 studs.
- Optionally add a colormap to set materials, or choose a Default Material to use one material everywhere. A colormap overrides the default material.
- Confirm the import to build the terrain inside the region.
Option 3: Script it with math.noise and WriteVoxels
If you want the game to build its own world, or you want a new map for each seed, write a script. This section gives you the rules plus a script you can run. For seeds, slope-based materials and more octave options, see our Roblox terrain generation page.
Voxel rules you need to know
- Terrain is made of voxels, 4x4x4 stud cubes that each have a material.
WriteVoxelsandReadVoxelsneed a resolution of **4** and a region aligned to the voxel grid.Region3:ExpandToGrid(4)handles the alignment for you.- One
WriteVoxelscall can cover at most 4,194,304 voxels. Go over that and it throws an error. - The materials and occupancy arrays must match the region size in voxels exactly. Occupancy goes from 0 to 1, and in-between values give smooth slopes.
- For simple shapes you can use
FillBlock,FillBall,FillCylinder,FillWedgeandFillRegion. Note thatFillBlockandFillBalloverwrite any terrain already inside their volume.
Generators that stack FillBlock columns on top of math.noise usually end up with a blocky, stepped look. Writing partial occupancy with WriteVoxels gives you smoother hills.
The math.noise gotcha
math.noise returns exactly 0 when every input is a whole number. If you pass raw stud positions like 4, 8, 12, you get a flat map. Divide by a scale first:
print(math.noise(4, 8)) --> 0
print(math.noise(4 / 100, 8 / 100)) --> a small non-zero numberNearby inputs give nearby outputs, which is why noise works well for hills and math.random does not. With fractional inputs, values mostly move between about -0.5 and 0.5, and the docs suggest math.clamp to keep results inside -1 to 1. Noise also repeats every 256 units, so if you pass a whole-number seed, seeds 256 apart give the same map. Multiplying the seed by a fraction, like the script below does, avoids that.
A working generator script
This script builds a 512x512 stud map. It works in 64-stud chunks and pauses between them so the server stays responsive.
local Terrain = workspace.Terrain
local SIZE = 512 -- map width in studs
local CHUNK = 64 -- chunk width in studs
local SCALE = 120 -- bigger number = wider, gentler hills
local HEIGHT = 60 -- max possible height in studs
local OCTAVES = 4 -- layers of noise, 4 to 6 works well
local SEED = 42 -- change for a new map
local RES = 4 -- WriteVoxels only accepts 4
local seedOffset = SEED * 0.618 -- fractional, so seeds 256 apart don't repeat
local function heightAt(x, z)
local total, amp, freq = 0, 1, 1
for _ = 1, OCTAVES do
total += amp * math.noise(x / SCALE * freq, z / SCALE * freq, seedOffset)
amp /= 2
freq *= 2
end
-- the 4-stud floor keeps the bottom layer solid, so there are no holes
return 4 + (math.clamp(total, -1, 1) + 1) / 2 * (HEIGHT - 4)
end
local function writeChunk(cx, cz)
local region = Region3.new(
Vector3.new(cx, 0, cz),
Vector3.new(cx + CHUNK, HEIGHT + RES, cz + CHUNK)
):ExpandToGrid(RES)
local size = region.Size / RES
local corner = region.CFrame.Position - region.Size / 2
-- one height per column
local heights = {}
for ix = 1, size.X do
heights[ix] = {}
for iz = 1, size.Z do
local x = corner.X + (ix - 0.5) * RES
local z = corner.Z + (iz - 0.5) * RES
heights[ix][iz] = heightAt(x, z)
end
end
local materials, occupancy = {}, {}
for ix = 1, size.X do
materials[ix], occupancy[ix] = {}, {}
for iy = 1, size.Y do
materials[ix][iy], occupancy[ix][iy] = {}, {}
local y = corner.Y + (iy - 0.5) * RES
for iz = 1, size.Z do
local h = heights[ix][iz]
local fill = math.clamp((h - y) / RES + 0.5, 0, 1)
local material = Enum.Material.Air
if fill > 0 then
material = if h > HEIGHT * 0.7 then Enum.Material.Snow
elseif h > HEIGHT * 0.5 then Enum.Material.Rock
else Enum.Material.Grass
end
materials[ix][iy][iz] = material
occupancy[ix][iy][iz] = fill
end
end
end
Terrain:WriteVoxels(region, RES, materials, occupancy)
end
for cx = 0, SIZE - CHUNK, CHUNK do
for cz = 0, SIZE - CHUNK, CHUNK do
writeChunk(cx, cz)
task.wait()
end
endWhat each part does:
heightAtadds four octaves of noise. Each one has double the frequency and half the strength of the one before, which puts small bumps on top of big hills. Our terrain page recommends 4 to 6 octaves, since one or two tends to look fake.- The height never drops below 4 studs, so the bottom voxel layer is always solid and players can't fall through holes in the map.
- Heights are worked out once per column, then reused for every voxel in that column.
- Each chunk is 16x16x16 voxels, far below the 4,194,304 cap.
fillis the partial occupancy. A voxel that the surface cuts through gets a value between 0 and 1, which smooths out the steps.- Material is picked by height: grass low down, rock higher up, snow on the peaks. Noise rarely reaches its extremes, so if you see little snow, lower the
0.7.
Where to run it
- Put it in a Script inside ServerScriptService, and the terrain builds when you press Play. Terrain made during Play is not saved to the place.
- To keep the terrain in the place, open the **Command Bar** (View tab, then Command Bar), paste the code and run it once in Edit mode. Then save or publish the place. Don't also keep the Script in ServerScriptService, or it will rewrite the terrain every time the server starts. For a map this small you can delete the
task.wait()line when running it from the Command Bar.
Keeping the server from freezing
In one [DevForum thread](https://devforum.roblox.com/t/faster-terrain-generation-help/1558814), a developer calling FillBall once per voxel for noise terrain got heavy server lag, and a reply pointed out that both WriteVoxels and the Fill methods are slow when used at a large scale. Things that help:
- Generate in small chunks with
task.wait()between them, like the script above. This helps a lot, but very large maps can still hitch, so keep chunks small and keep yielding. - Run the noise math in parallel with **Actors**, which is what the thread suggested.
ReadVoxelsis thread-safe, butWriteVoxelsand the Fill methods are not, so do the actual writing serially.
Plugins and paid generators
- TerrainSuite is a free Studio plugin posted in July 2026. It has presets (Alpine Valley, Moon Craterfield, Lava Fields, plus Tropical Island and Desert Plain from a September 2026 update), map-style biome regions that blend at the borders, and a quality report. It runs locally with no external calls. Caves and scattering are planned but not out yet. Use it if you want blended biome regions without writing code.
- A Voxel Terrain Generator .rbxl sells on itch.io starting at $4. Use it if you want a ready-made place file to study or build on.
- The Infinite Terrain Generator is a third-party script on BuiltByBit for $3.99. It is seed-based and runs on the client only, so a multiplayer game needs its own replication. Use it only if you are ready to handle that part yourself.
When the land is finished, set up lighting and atmosphere so it looks good, and see our post on Roblox 3D model generation for props to place on top.
Getting a terrain script written with Picoo
The script above only picks materials by height. If you want more, [Picoo](https://picoo.io/) can write a Luau terrain generator from a description, with materials chosen by height and slope (rock on cliffs, grass on flat ground, sand at the waterline), water filled up to sea level by turning only Air voxels into Water, and biome presets like desert dunes, tropical island, mountain range, volcanic and rolling plains. It is set up to write only the region you ask for instead of calling Terrain:Clear(), but still read the script before you run it on a place that already has terrain. Picoo is a paid tool: Pro is $19/mo (or $12/mo billed yearly) for 200 credits a month and unlimited projects, and Business is $49/mo (or $35/mo billed yearly) for 500 credits a month.
FAQ
Why is my math.noise terrain completely flat?
You are most likely passing whole numbers. math.noise returns 0 when all inputs are integers, so divide your x and z positions by a scale such as 100 before you pass them in.
Why does WriteVoxels throw an error?
The usual causes are a resolution other than 4, a region that is not aligned to the voxel grid (use ExpandToGrid(4)), arrays that don't match the region size in voxels, or a region larger than 4,194,304 voxels.
How do I make a bigger map?
Raise SIZE and keep it a multiple of CHUNK. Chunking keeps each WriteVoxels call small, but bigger maps take longer to build and can still hitch. All terrain has to fit inside Terrain.MaxExtents, which runs from -32000 to 32000 cells on each axis, with each cell 4 studs.
How do I add water?
Pick a sea level and turn empty voxels below it into water inside the same loop, right after material and fill are set:
local SEA_LEVEL = 20 -- studs, put this at the top of the script
if fill == 0 and y < SEA_LEVEL then
material = Enum.Material.Water
fill = 1
endFor beaches, use Enum.Material.Sand for land where h is just above SEA_LEVEL.