npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@rbxts/serio

v1.0.19

Published

Roblox buffer serialization library

Readme

Serio

Binary (buffer) serialization library for Roblox built on Flamework.

Heavily inspired by @rbxts/flamework-binary-serializer. It generates schemas for ser/des from just a type just like FBS.

[!CAUTION] Depends on rbxts-transformer-flamework!

Getting started

Schemas

Schemas in Serio are just types. The most basic of schemas is just a singular type, such as boolean, u8, etc. However schemas can also contain objects, lists, sets, maps, tuples, and several Roblox data types. As an example:

import type { u8 } from "@rbxts/serio";

interface MySchema {
  readonly epic: boolean;
  readonly foo: u8;
}

Serializers

Only one (global) function is exposed by Serio, createSerializer(). It's a macro you will pass your schema into to create a serializer. Serializers have two methods. serialize() and deserialize(). Serialized data contains a buffer (buf), and a list of blobs (blobs) (values which can not, or should not, be serialized. e.x. instances)

import createSerializer from "@rbxts/serio";

const mySerializer = createSerializer<MySchema>();
const data = mySerializer.serialize({ epic: true, foo: 69 });
const result = mySerializer.deserialize(data);
print(data.buf, data.blobs);
print(result.epic, result.foo);

Data Types

Serio can serialize many data types, including numeric types not natively supported by the buffer library. Namely

Safety

Serio automatically asserts that inputs are within the bounds of their respective type. (e.x. u8 only allows numbers 0 to 255)

Extended Support

Serio supports data types that are not natively supported by the buffer library or Roblox. Currently the only examples of this are f24,f16, u24, and i24.

import type { f16, f24, u24, i24 } from "@rbxts/serio";

interface CoolTypes {
  readonly a: f16;
  readonly b: f24;
  readonly c: u24;
  readonly d: i24;
}

Customization

Serio encourages full customization over the size of serialed values.

import type { List, String, HashSet, HashMap, Tuple, Vector, ScaleOffset, ScaleOffset2, i8, u8, i16, u16 } from "@rbxts/serio";

interface Example {
  // serialize the length of the array/string/tuple/set/map as a u8, allowing a maximum of 255 elements (which most collections are under anyways)
  list: List<string, u8>;
  string: String<u8>;
  set: HashSet<"a" | "b" | "c", u8>;
  map: HashMap<string, i8, u8>;
  tuple: Tuple<[boolean, u8, string], u8>;

  // serialize X and Z as i16s but Y as a u16, allowing a range of:
  // X: -32,768 - 32,767
  // Y: 0 - 65,535
  // Z: -32,768 - 32,767
  positiveYVector: Vector<i16, u16, i16>;
  velocity: Vector<i8>; // serialize X, Y, and Z as i8s

  // serialize positional X, Y, and Z as i16s
  cframe: Transform<i16>;

  // serialize Scale as an f32, and Offset as a u8
  udim: ScaleOffset<f32, u8>;
  // serialize X.Scale and Y.Scale as f32s, and X.Offset and Y.Offset u8s
  udim2: ScaleOffset2<f32, u8>;
  // serialize X.Scale as an f32, Y.Scale as a u8, X.Offset as a u8, and Y.Offset as an i8
  specificUDim2: ScaleOffset2<f32, u8, u8, i8>;
}

const serializer = createSerializer<Example>();

Packing

Serio can bitpack your data for you using the Packed<T> datatype. It bitpacks every type under T recursively.

You may know a boolean can be represented by just one bit, but to insert a boolean into a buffer you need to serialize it as an entire byte (8 bits). This is where bitpacking comes in. We can keep track of a list of bits to later combine together into a single byte, rather than one byte for each of your booleans.

This doesn't just affect booleans though, it also affects:

  • Optional values
    • Boolean for whether the value exists
  • UDim2s
  • Vector3s
  • CFrames
    • Optimization for two vector special cases (0,0,0 and 1,1,1) and axis aligned rotation special cases
    • Boolean for whether the position was optimized
    • Boolean for whether the rotation was optimized

Supported Types

  • All primitives
  • Tuples
  • Objects
  • Array
  • Map<K, V>
  • Set
  • CFrame
  • Vector3
  • UDim
  • UDim2
  • Color3
  • ColorSequence
  • NumberSequence
  • Literal unions
  • Enums

Optimization

CFrames use 6 bytes less than in FBS by default. In FBS CFrames are serialized as six f32s, 24 bytes. The default for Serio is three f32s for position, two u16s for rotation X/Z, and one i16 for rotation Y. And of course you can make this less by not using the default type for position X/Y/Z (Transform<X, Y, Z>).