Skip to content


Range Syntax

In some cases using the entire range of numbers isn't desirable and you'd rather limit the range. This can be done by using Zap's range syntax. This syntax will be very similar for users of Rust.

A full range is written by giving a minimum and maximum, separated by two dots. This looks like 0..100.

One sided ranges are ranges where only the minimum or maximum is given. These look like 0.. or ..100.

Exact ranges can be written by only giving the exact number, such as 0 or 100.

Ranges can be written with no constraints by giving no minimum or maximum. This looks like ...

As a recap:



Remember that ranges are always inclusive on the min and max.


Zap supports all buffer number types, signed integer, unsigned integer, and floating point.

Signed Integers

Signed integers are standard integers. They can be positive and negative.

TypeMin ValueMax Value

Unsigned Integers

Unsigned integers are positive integers.

TypeMin ValueMax Value

Floating Point Numbers

Floating point numbers are numbers with a decimal point. They can be positive and negative.

Buffers support f32 and f64 floating point numbers, but unlike integers these numbers do not have a hard range. Instead the size determines the precision of the number. Determining what precision you need is out of scope for this documentation.

f64s are able to store integers accurately up to 2^53 (9,007,199,254,740,992). This is larger than the maximum value of u32, but also twice the size.

It should also be noted that the type of numbers in Luau is f64.

Constraining Numbers

Numbers can be constrained by placing a range within parenthesis after the number type. For example, if you wanted to constrain a u8 between 0 and 100 you could do:


Strings are defined using the word string. For example:

The length of strings can be constrained by placing a range within parenthesis after the string keyword. For example, if you wanted to constrain a string between 3 and 20 characters (like a username) you could do:


Arrays are defined as a type followed by square brackets. For example an array of u8s would be:

The length of arrays can be constrained by placing a range within the square brackets. For example if you wanted to constrain the length of an array between 10 and 20 items you could do:


Maps are objects that have keys of one type, and values of another type.

Maps are defined using the map keyword, followed by a Luau-like map syntax. For example, a map of string keys and u8 values would look like:


Zap has two types of enums, unit enums and tagged enums.

Unit Enums

Unit enums are used to represent a set of possible values. They are defined using the enum keyword, followed by a set of possible string values. For example, a unit enum representing the status of a round would look like:

This code would then create the Luau type:

type RoundStatus = "Starting" | "Playing" | "Intermission"

Tagged Enums

Tagged enums will be very familiar to Rust users.

Tagged enums are a set of possible variants, each with attached data. They are defined using the enum keyword, followed by a string which is the tag field name. After the tag field name, a set of variants are defined. Each variant is defined by a string tag, followed by a struct. Variants must be separated by a comma. Trailing commas are allowed.

This code would create the Luau type:

type t = { type: "number", value: number }
	| { type: "string", value: string }
	| { type: "boolean", value: boolean }

Tagged enums allow you to pass different data depending on a variant. They are extremely powerful and can be used to represent many different types of data.


Structs are similar to Interfaces, and are a collection of statically named fields with different types.

To define a struct, use the struct keyword followed by a Luau interface-like syntax. For example, a struct representing an item in a shop would look like:


Roblox Instances can be passed through Zap.


If a non-optional instance results in nil when received, it will cause a deserialize error and the packet will be dropped. Instances are turned into nil when they don't exist on the reciever - for example: an instance from the server that isn't streamed into a client or an instance that only exists on the client.

If you want to send an instance that may not exist, you must make it optional.

You can also specify what kind of instance you want to accept, for example:

Classes that inherit your specified class will be accepted, for example Part.


Zap supports sending CFrames. There are two types of CFrame you may send - a regular CFrame, and an AlignedCFrame.

CFrame rotation matrices are compressed using the axis-angle representation.


CFrames are orthonormalized when sent. If you need to send a CFrame that is not orthogonal, i.e. one that does not have a valid rotation matrix, it is recommended to send the components and reconstruct it on the other side. Note that use cases for this are exceedingly rare and you most likely will not have to worry about this, as the common CFrame constructors only return orthogonal CFrames.

Aligned CFrames

When you know that a CFrame is going to be axis-aligned, it is preferrable to use the AlignedCFrame type.

It uses much less bandwidth, as the rotation can just be represented as a single byte Enum of the possible axis aligned rotations.

You can think of an axis-aligned CFrame as one whose LookVector, UpVector, and RightVector all align with the world axes in some way. Even if the RightVector is facing upwards, for example, it would still be axis-aligned.

Position does not matter at all, only the rotation.


If the CFrame is not axis aligned then Zap will throw an error, so make sure to use this type carefully! Don't let this dissuade you from using it though, as the bandwidth savings can be significant.

Here are some examples of axis-aligned CFrames.

local CFrameSpecialCases = {
	CFrame.Angles(0, 0, 0),
	CFrame.Angles(0, math.rad(180), math.rad(90)),
	CFrame.Angles(0, math.rad(-90), 0),
	CFrame.Angles(math.rad(90), math.rad(-90), 0),
	CFrame.Angles(0, math.rad(90), math.rad(180)),
	-- and so on. there are 24 of these in total.

Other Roblox Classes

The following Roblox Classes are also available as types in Zap:

  • Vector3

Optional Types

A type can be made optional by appending a ? after the whole type, such as: