Your First Mod in 5 Minutes
What You’ll Build
By the end of this tutorial, you’ll have a working Rawframe mod that spawns a rotating cube in the world. No prior modding experience needed — just a text editor and a Rawframe installation.
Step 1: Create the Mod Folder
Every mod lives in the mods/ directory. Create a new folder and a mod.json manifest:
mods/
my-first-mod/
mod.json
scripts/
server/
init.lua
Your mod.json tells the engine about your mod:
{
"name": "my-first-mod",
"version": "1.0.0",
"title": "My First Mod",
"author": "YourName",
"description": "A spinning cube to learn the basics.",
"scripts": {
"server": ["scripts/server/init.lua"]
}
}
Tip: You can also run
bash tools/create_mod.sh MyFirstMod "YourName"to scaffold this automatically.
Step 2: Write Your First Script
Open scripts/server/init.lua and add:
-- Spawn a cube 5 units above the ground
local cube = entity.create("my_cube")
entity.set_position(cube, Vec3.new(0, 5, 0))
entity.set_mesh(cube, "primitive:cube")
-- Track rotation angle
local angle = 0
-- Rotate the cube every frame
hook.Add("Think", "SpinCube", function(dt)
angle = angle + dt * 90 -- 90 degrees per second
entity.set_rotation(cube, Quat.from_euler(0, angle, 0))
end)
print("My first mod loaded!")
That’s it — 10 lines of code. Let’s break it down:
entity.create()spawns a new entity with a nameentity.set_position()places it in the world using aVec3entity.set_mesh()gives it a visual mesh (built-in primitives start withprimitive:)hook.Add("Think", ...)registers a function that runs every server tickQuat.from_euler()creates a rotation from pitch, yaw, and roll angles
Step 3: Run and Test
Start a local server with your mod loaded:
./bin/rawframe_server -port 27015
The server automatically loads mods from the mods/ directory. You should see My first mod loaded! in the console output.
Now connect with the client:
./bin/rawframe_client -connect 127.0.0.1:27015
You’ll see a cube floating in the air, spinning smoothly at 90 degrees per second.
Step 4: Add Networking
Let’s make it interactive. When a player presses a key, the cube changes color. Add a client script at scripts/client/input.lua:
-- Client-side: detect key press and send to server
hook.Add("Think", "CheckInput", function()
if input.is_key_pressed("E") then
net.send("ChangeColor")
end
end)
Update your mod.json to include the client script:
{
"scripts": {
"server": ["scripts/server/init.lua"],
"client": ["scripts/client/input.lua"]
}
}
Then handle the message on the server in init.lua:
-- Server-side: receive the message and change color
local colors = {
Vec3.new(1, 0, 0), -- red
Vec3.new(0, 1, 0), -- green
Vec3.new(0, 0, 1), -- blue
}
local color_index = 1
net.receive("ChangeColor", function(peer)
color_index = (color_index % #colors) + 1
entity.set_color(cube, colors[color_index])
end)
Now when any player presses E, the cube cycles through red, green, and blue.
What’s Next?
You’ve just created a mod with entity spawning, physics-driven updates, and client-server networking. From here, you can:
- Add physics — use
physics.set_body()to give entities rigidbodies - Build UI — the
ui.*API has 250+ functions for creating in-game interfaces - Create weapons — the
weapon.*API supports hitscan, projectile, and melee weapons - Store data —
storage.*provides per-mod SQLite persistence
Check out the API documentation for the full list of 583+ functions, or browse example mods in the mods/ directory for inspiration.
Happy modding!