Skip to content
Back to Blog

Your First Mod in 5 Minutes

By Rawframe Team
tutorial modding beginner

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 name
  • entity.set_position() places it in the world using a Vec3
  • entity.set_mesh() gives it a visual mesh (built-in primitives start with primitive:)
  • hook.Add("Think", ...) registers a function that runs every server tick
  • Quat.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 datastorage.* 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!