Weaponizing float precision loss

The far lands

Go far enough from the origin in Roblox and your character starts deforming. Limbs detach, seams breathe, the camera shakes on its own. Most people know this as "the far lands" and treat it as a bug.

After digging into the cause of this and all of it's aspects, I find this pretty pleasing and wondered if I could take advantage of this to make something unique which looks nice.

Here's what it actually looks like in Studio:

Precision loss demo in Roblox Studio. UI generated by Opus 5.

The root cause

Roblox stores every CFrame and Vector3 in 32-bit floats. Luau numbers are 64-bit doubles, so your math is exact right up until you assign it into a CFrame, at which point it gets truncated.

A float32 is 32 bits split into three fields:

[ 1 sign ][ 8 exponent ][ 23 mantissa ]

Read more about how a float32 works here.

Since the mantissa is a fraction with 23 bits after the binary point, the smallest amount it can change by is 2^-23. Multiply that by the number's exponent and you get the gap between two neighbouring representable numbers. This would be the ULP (Unit in the Last Place):

ULP(x) = 2^-23 × 2^floor(log2(x))
       = 2^(floor(log2(x)) - 23)

And 2^23 = 8,388,608, which is why the same thing can be written as a plain division:

ULP(x) ≈ x / 8388608

The floating point error is proportional to your distance from the origin. Everything else in this post follows from that one fact.

The exact thresholds

Precision halves at every power of two, so the degradation is stair-stepped, not smooth. It gets visibly worse the instant you cross 65536, 131072, 262144, and so on.

Coordinate range ULP (offset in studs)
< 8,192 < 0.001
8k – 65k 0.001 – 0.0078
65k – 262k 0.0078 – 0.031
262k – 1M 0.031 – 0.125
1M – 4M 0.125 – 0.5
4M – 16M 0.5 – 2
> 16.7M (2^24) > 2

The detail that matters most for working with this is that each axis is a separate float with its own exponent. At (1000000, 0, 0) the Y and Z components are still perfectly precise. Only X jitters. The glitch is directional if you want it to be.

Here's a 2.5 stud circle (about the size of a character) placed at whatever distance you pick. Every point gets rounded to float32 the same way a Vector3 would. Drag it further out and watch it snap:

Bottlenecks with this design

If this were to be an aesthetic within a game, you must keep in mind that raycasts would also be subject to this floating point error. Raycast results and .Magnitude checks would be inaccurate depending on your distance from the origin.

The amplitude formula in Luau

Given a position, how much wobble do you actually get? Per axis, independently:

ULP(v) = 2 ^ (floor(log2(|v|)) - 23)

In Luau:

local function ulp(v: number): number
	local _, exp = math.frexp(math.abs(v))
	return 2 ^ (exp - 24)
end

local function amplitude(pos: Vector3): Vector3
	return Vector3.new(ulp(pos.X), ulp(pos.Y), ulp(pos.Z))
end

Side note explanation for math.frexp

math.frexp returns exp as floor(log2(v)) + 1, hence the -24. This function returns the mantissa and exponent of a number.

Amplitude returns the offset in studs of how much it can jitter from the lowest bound to the highest. To get the jitter from the origin, simply divide it by 2 (amplitude(pos) / 2).

Faking it at the origin

It sucks needing to give up floating point precision to achieve this effect. What you can do is just quantize positions to a grid. This basically snaps any given position to a grid, making it possible to reproduce it exactly, at any strength, anywhere in your map:

local function snap(n: number, step: number): number
	return math.floor(n / step + 0.5) * step
end

local function quantize(pos: Vector3, step: Vector3): Vector3
	return Vector3.new(
		snap(pos.X, step.X),
		snap(pos.Y, step.Y),
		snap(pos.Z, step.Z)
	)
end

Apply it per component to a part's world position, with step as your intensity dial. Randomize which parts are affected each frame and it flickers instead of buzzing.


After all this research, I was not able to find a use case or an aesthethic way to style this. But I'm sharing it here because I think it's interesting to know how floating point precision works and how it can be used to create a unique effect that someone else may find useful.