Noise Class: Random Class + math.noise

Introduction

A while back I made a handy tool for procedural generation / noise based mechanics! Been meaning to share it for a while. To understand it though - you need to understand two prior bits of Roblox / lua technology.

math.noise

This is math.noise:

local value = math.noise(x,y)

when plotting this value across a 2d plane it looks like this
image

it’s really handy for things like terrain generation, adding complexity to textures, and many different forms of procedural generation.

Random Class

This is the Random Class:

local seed = 3
local random = Random.new(seed)
local value = random:NextNumber() -- returns a rand number from 0-1

It’s better in many ways than math.random because you can seed specific instances of it. This means that you can have it send the same random numbers in the same random order. This is called being “deterministic” and is very helpful for when you want to synchronize two different systems (or player clients).

There is a function called math.randomseed traditionally in lua, however due to it being a global seed it can fail to remain deterministic pretty easily when multiple threads call it.

Introducing: Noise Class

This is where my class comes in. It takes the deterministic elegance of the Random Class, and combines it with the utility provided by math.noise!

Currently, it supports 4 different noise maps in both 2D and 3D:

Random
image

Perlin (exact same as math.noise, but now deterministic!)
image

Worley / Cellular
image

Voronoi
image

Here’s how you might use the perlin one:

local noise = Noise.new(12356)
local value = noise:Perlin(x,y) -- a value from 0 to 1 (ish)

reminder, this is the same as:

local value = math.noise(x,y) -- also a value from 0 to 1 (ish)

except of course - with that seed number you will get the exact same random numbers in the exact same order using Noise.

Benchmarks

So - how fast do we compare to math.noise?

Each map had the generation function called 32x32=1024 times, with the benchmark measuring how long it took to run to completion. You can find the bench script here if you want to reproduce it.

The math.noise function using just an x and y parameter is the fastest. If you don’t need determinism and value speed, use that. It runs at around ~5000 operations/ops per millisecond/ms.

Compared to math.noise:

  • perlin 2D: 2x slower at ~2000 ops/ms.
  • random 2D: 4.5x slower at ~1000 ops/ms.
  • celullar 2D: 6x slower at ~800 ops/ms.
  • voronoi 2D: 6.5x slower at ~770 ops/ms.

3D in general tends to slow things by 50-100% on average (except for Random, which is as low as <30% due to each dimension being solved independently). Thankfully, there are way more use cases for 2D than 3D in my experience.

As I find new optimizations I’ll of course add them, however these operate fast enough for most of my use cases - and realistically, the bottleneck will usually be when you update the instances to match the noise values, not the noise itself.

Where to Get It

Contributing

Would love to see some new noise maps added, as well as optimizations to the prior ones (except for Perlin - that’s literally a translation of the real math.noise code from C++ to luau for ez language perf comparison).

That’s all! Thank y’all for reading and I hope this helps ya with your projects!

17 Likes

This is certainly very useful, especially since coding each different noise generation methods is a bit annoying, this seems to be pretty convenient.

Only question I have is why there’s this weird streak in the Perlin noise generation image you provided?
image

So, I actually don’t know haha - for the sake of backwards compatability, the perlin implementation was literally taken from the official Roblox luau source code:

It’s of course possible I messed up the translation, but while testing it I was comparing to math.noise and I didn’t notice it desync. That being said, I am interested in porting Simplex noise at some point - it tends to look like less of a grid which can be handy.

1 Like

Can’t believe this gem went almost 2 weeks without a single reply, this looks awesome!

It also seems like Roblox is working on their own Noise class but it’s currently locked :frowning:
It looks like it supports only “SimplexGabor” noise type right now, but they may have more noise types planned. Also the ability to sample noise directionally sounds cool. I hope they add it.

This would be really cool to see done natively, but great work on this module! I was actually looking for efficient noise generation recently, and the algorithms you provided may be able to help.

2 Likes

I hadn’t heard - just my luck haha. Honestly it’s for the best - there are so many speed benefits to having it written in C++. Excited for them to release it!

2 Likes