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:
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.