RoFlux

One way syncing from your editor into Roblox Studio. The server owns the truth, Studio only ever receives.

Write code where you want to write it

Keep your game on disk, in real files, under version control, in the editor you already use. RoFlux watches the folder and pushes every change into Studio as it happens.

$ roflux serve
     ok TestProject loaded, 24 instances, 3 scripts
   info watching C:\dev\TestProject
     ok listening on http://127.0.0.1:34872
   sync studio connected (1 total)
   sync +1 ~1 -0 (25 instances, 3 scripts)
        + ReplicatedStorage/Shared/Inventory [ModuleScript]
        ~ StarterPlayer/StarterPlayerScripts/Client (Source)

What makes it different

One direction, always

Studio never tells the server anything about the place. Files are the source of truth, so there is no merge, no conflict, and no chance of Studio quietly overwriting your work.

It only touches what you declared

Every instance is managed, passthrough or a reference. RoFlux deletes drift inside the trees you own and leaves everything else in the place completely alone.

Scripts that run at compile time

Hook into reads, writes and file events. Rewrite source on its way out, or claim a file extension and turn it into a whole tree of instances.

The sourcemap is the model

sourcemap.json is regenerated on every rebuild and is what luau-lsp reads, so types and autocomplete follow your files without a second tool.

Place and model files

roflux build writes real .rbxl and .rbxm files with properly typed properties, so the same project can go straight to a build pipeline.

The plugin ships in the binary

roflux plugin installs the Studio plugin from the server executable itself, so the plugin you are running always matches the server you are running.

A project is just files

There is no giant manifest describing your tree. A file becomes an instance, a folder becomes a folder, and a small meta.json says where a tree belongs and what it is.

MyGame/
├─ default.project.json      -- project info and service properties
├─ scripts/                  -- compile time hooks
│  └─ banner.luau
├─ Shared/
│  ├─ init.meta.json         -- { "$Parent": "ReplicatedStorage" }
│  ├─ Inventory.luau         -- ModuleScript
│  └─ Signal/
│     ├─ init.luau           -- the folder becomes a ModuleScript
│     └─ Connection.luau     -- a child of it
└─ Server/
   ├─ init.meta.json         -- { "$Parent": "ServerScriptService" }
   └─ Main.server.luau       -- Script

Read the project format →

Turn any file into instances

A hook script can claim an extension and return a declaration. One file can build a whole subtree, and every instance it produces is traced back to that file, so editing it patches only what changed and deleting it removes everything it made.

roflux.transpile("html", function(file)
	local screen = { ["$ClassName"] = "Frame" }

	for tag, body in file.text:gmatch("<(%a[%w]*)[^>]*>%s*([^<]*)%s*</") do
		screen[tag] = {
			["$ClassName"] = "TextLabel",
			["$Properties"] = { Text = body },
		}
	end

	return screen
end)

Read about the script system →

Three commands to get going

-- build the server
cargo build --release --manifest-path Server/Cargo.toml

-- install the Studio plugin out of the binary
roflux plugin

-- start syncing
roflux serve MyGame

Then open Studio, click the RoFlux button, and press Connect.

Full setup →