# Optimizing procedurally generated terrain

Currently I am creating a infinite generation game like Minecraft. The code I have written works how I want it too, creating chunks, unloading chunks and storing them. However, If I want to make multiple layers of ground so you can mine underground like in Minecraft, It lags terrible and I have no idea how to optimize this.
How can I optimize my blocks in a procedurally generated world with multiple block layers and only load blocks that the player can see and update the terrain whenever a block is mined to load the new blocks?

``````
local size = 3
local chunk_size = 15
local octaves = 6
local amplitude = 10
local frequency = 30
local L = 2
local gain = 1.4

function getHeight(x, z, X, Z)

local y = 0
local amp = amplitude
local freq = frequency

for i = 1,octaves do
y = y + (math.noise((X*chunk_size+x)/freq, (Z*chunk_size+z)/freq) * amp)
freq = freq*L
amp = amp*gain
end

return y

end

game.ReplicatedStorage.World.CreateChunk.OnServerEvent:Connect(function(plr, chunkX, chunkZ)

local chunkModel = Instance.new("Model", workspace.ActiveChunks)
chunkModel.Name = "Chunk"
local XValue, ZValue = Instance.new("NumberValue", chunkModel), Instance.new("NumberValue", chunkModel)
XValue.Name = "x"
ZValue.Name = "z"
XValue.Value = chunkX
ZValue.Value = chunkZ
local loaded = false
local loader = Instance.new("ObjectValue", chunkModel)

for i, v in pairs(game.ReplicatedStorage.StoredChunks:GetChildren()) do
if v:FindFirstChild("x") and v:FindFirstChild("z") then
if v.x.Value == chunkX and v.z.Value == chunkZ then
v.Parent = workspace.ActiveChunks
v.owner.Value = plr
end
end
end

if not loaded then
for x = 1, chunk_size do

for z = 1, chunk_size do

local height = math.floor(getHeight(x, z, chunkX, chunkZ))*size
local block = game.ReplicatedStorage.BlockDatabase.Grass:Clone()
local items = 0

local y = 0
if height/3 > -1 then

block.Parent = chunkModel
block.Position = Vector3.new((chunkX*chunk_size)*size+chunk_size+(x*size), height+y*size, (chunkZ*chunk_size)*size+chunk_size+(z*size))
end
if height/3 < 0 then
local water = game.ReplicatedStorage.BlockDatabase.Water:Clone()
water.Parent = chunkModel
water.Position = Vector3.new((chunkX*chunk_size)*size+chunk_size+(x*size), -.5+y*size, (chunkZ*chunk_size)*size+chunk_size+(z*size))
end
if height/3 < math.noise(x*.02, z*.02)*15 and height/3 > -1  then
block:Destroy()
local sand = game.ReplicatedStorage.BlockDatabase.Sand:Clone()
sand.Parent = chunkModel
sand.Position = Vector3.new((chunkX*chunk_size)*size+chunk_size+(x*size), height+y*size, (chunkZ*chunk_size)*size+chunk_size+(z*size))
end
if height/3 > math.clamp(math.noise(x*.02, z*.02), -.5, .5)*20 + 20 then
block:Destroy()
local stone = game.ReplicatedStorage.BlockDatabase.Stone:Clone()
stone.Parent = chunkModel
stone.Position = Vector3.new((chunkX*chunk_size)*size+chunk_size+(x*size), height+y*size, (chunkZ*chunk_size)*size+chunk_size+(z*size))
end
if height/3 > math.clamp(math.noise(x*.02, z*.02), -.5, .5)*30 + 35 then
block:Destroy()
local stone = game.ReplicatedStorage.BlockDatabase.Snow:Clone()
stone.Parent = chunkModel
stone.Position = Vector3.new((chunkX*chunk_size)*size+chunk_size+(x*size), height+y*size, (chunkZ*chunk_size)*size+chunk_size+(z*size))
end

end

end
end

end)
``````

This is the main script for creating chunks.

Only create parts for the “outer layer” of blocks, i.e. blocks that have at least 1 air block neighbor. When a block is mined, update it to see if it has become part of the outer layer.