Build worlds. Ship them in minutes.

Voxa Studio is a free 3D world editor. Drop in parts, write Luau scripts, hit Publish β€” your world goes live for everyone on Voxa to play.

🌍

Your worlds

β€”

🀝

Friends

β€”

❀️

Favorites

β€”

πŸ“ˆ

Worlds on Voxa

β€”

Quick-start templates

Open in Voxa Studio and Publish β€” that's it.

β—»

Empty

A blank canvas with a spawn point. Build whatever you can imagine.

β–¦

Baseplate

A flat ground plane. Drop parts onto it and build out.

β–²

Obby Starter

A jumping obstacle course with kill bricks and a goal.

βŒ‚

Lobby

Walls, floor, and a glowing centerpiece. Drop in spawn portals.

Creations

Your published worlds.

⏳

Loading...

Reading your worlds from Voxa.

Asset Library

Upload clothing, avatars, body parts, accessories, and animations for the Voxa community.

Format guide

Shirt / Pants: 1024Γ—1024 .png β€” must be square. A non-square image stretches and lands in the wrong place on the body. Paint on the template so each area maps correctly.

Face: 1024Γ—1024 .png β€” eyes & mouth go on the head-front panel (top-left). Use the face template so features line up.

Hair: .glb / .gltf. Origin at head bone, facing βˆ’Z.

Decal: .png / .jpg. Used as face image on parts.

Video: .mp4 / .webm, max ~50 MB.

Sign in (top-right) with your Voxa account β€” uploading needs your account.

My uploads

Your published assets.

⏳

Loading...

Gamepasses

Sell premium features inside your worlds. Players buy them with VoxUs.

How it works

Three steps to your first paid gamepass.

  1. Open your world in Voxa Studio β€” the world has to be published before you can attach gamepasses to it.
  2. Click 🎟 Gamepasses in the Studio toolbar. Give your pass a name, description, and price in VoxUs.
  3. Copy the pass ID. Paste it into a script like prompt_gamepass("pass_123…") to pop up the buy dialog when the player touches a part β€” or just let it sit on the world detail page where anyone can buy it.

VoxUs the buyer spends is credited to the world creator's balance immediately. When real-money VoxUs purchases come online, creator earnings will flow through this same path.

In-script API

Commands you can use inside Studio scripts.

-- Pop up the buy dialog when the player touches this part
function on_touch(other)
  prompt_gamepass("pass_1234567890")
end

-- Fire a custom event only if the player owns the pass
function on_start()
  if_owns_gamepass_fire("pass_1234567890", "vip_door_open")
end

Earnings

Cash out the VoxUs you've earned from players buying your items and gamepasses.

β€” VoxUs earned & available to cash out

Only VoxUs earned from sales counts here. VoxUs you bought or were gifted can be spent in Voxa but can't be cashed out. Minimum payout is 1,000. We review every request by hand, so allow a few days.

You must own this account and be allowed to receive payments where you live. We may ask you to confirm your identity before paying out, and we report earnings as required by law.

Analytics

Per-world plays, session length, and where players come from.

Loading analytics…

Learn & get help

Docs, scripting reference, and the community.

πŸ“˜

Studio basics

Move parts, set materials, paint colors, and group objects together.

Read guide β†’
⚑

Luau scripting

The full reference: syntax, functions, lists and tables, events, and all 67 commands.

Read reference β†’
πŸ› 

Test mode

Walk through your world without publishing. Scripts run, scripts print, you see it live.

Watch demo β†’
πŸ’¬

Community

Share what you're building, get feedback, and find people to play with.

Join Discord β†’
β˜…

Featured worlds

See what other creators are publishing on Voxa right now.

Browse trending β†’
⌘

Keyboard shortcuts

Q select Β· M move Β· R rotate Β· T scale Β· Shift+A anchor Β· Ctrl+D duplicate.

View all β†’

Scripting reference

Everything you can write in a Script object β€” syntax, rules, and all 67 commands.

Your first script

In Studio, insert a Script, paste this, and press β–Ά Test. Voxa scripting is Luau-flavored: it reads like Roblox code, and it runs sandboxed, so a script can never touch a player's computer.

-- runs once when the world loads
function on_start()
  show_toast("Hello Voxa!")
  spawn_part("Floor", "Block", 0, 0, 0, 20, 1, 20, 0.3, 0.7, 0.4)
end

-- runs when a player touches this script's part
function on_touch()
  show_toast("You touched it!")
  win()
end

Basics

One statement per line. Comments start with --. Variables need no type.

score = 0
name  = "Voxa"
msg   = "Score: " + str(score)   -- + joins text; str() turns numbers into text

Operators are written like this β€” note these are not the Lua ones:

+  -  *  /  %        math (% is remainder)
==  !=  <  >  <=  >=  comparing  (use != , NOT ~=)
and  or  not          logic      (NOT && || !)

Handy built-ins you can use inside any expression: abs min max clamp floor ceil round sqrt sin cos randi randf str, plus .size() on a list or table. For a random 0–5: randi() % 6.

If & loops

if score > 10 then
  show_toast("Nice!")
elseif score > 5 then
  show_toast("Getting there")
else
  show_toast("Keep going")
end

for i = 0, 9, 1 do        -- start, stop (included), step
  print(i)
end

while hp > 0 do
  hp = hp - 1
end
The #1 mistake: you cannot write an if on one line. if x then y end will not work. if … then must be alone on its line, the body on the lines below, and end on its own line. Same for for and while.

Loops stop automatically after 100,000 rounds, so a runaway loop can't freeze anyone's game.

Lists & tables

nums = [10, 20, 30]      -- a list
nums[1] = 99             -- change an item (counting starts at 0)
push(nums, 40)           -- add to the end   (pop(nums) removes the last)
count = nums.size()

t = {}                   -- a table: store anything under a key
t["hp"] = 100
t[row * 8 + col] = 3     -- keys can be math β€” perfect for a grid

grid = [[1, 2], [3, 4]]  -- lists inside lists work too
grid[1][0] = 30
Lists and tables are shared, not copied. If you hand one to a function, or read a global one inside a function, it's the same object β€” so changing an item changes it everywhere. That's how functions update shared data.

Functions

function add(x, y)
  return x + y
end

total = add(3, 4)   -- use the result
do_thing()          -- or just run it
Calling rule: a function call must be on its own line, or be the entire right side of an =. It can't sit inside a bigger expression.

βœ— if adjacent(a, b) == 1 then    βœ— n = 1 + score_of(p)
βœ“ adj = adjacent(a, b) then if adj == 1 then

Scope (where variables live)

Code in an event like on_start makes global variables. Code inside a function you wrote makes local ones that disappear when it finishes β€” so a function can't change a global number directly.

-- βœ— does nothing to the real score
function bump()
  score = score + 1
end

-- βœ“ keep shared data in a table, which IS shared
function on_start()
  G = {}
  G["score"] = 0
end

function bump()
  G["score"] = G["score"] + 1
end

Events

EventWhen it runs
on_start()Once, when the world loads.
on_tick()Every frame. Great for spinning or moving things.
on_touch()A player touches the part this script is on.
on_activate()A Tool is clicked while a player holds it.
on_<button>()A GUI button was clicked β€” named after the button.
on_click()Any GUI button was clicked. Read clicked (its name) and clicked_num (its trailing number) to tell which.
on_<name>()Something called fire("name").

Lines written outside any function run as part of on_start. fire("my_event") runs on_my_event() on every script in the world β€” that's how scripts talk to each other.

Making a GUI

Create a screen once, then add elements to it. A button fires an event named exactly after the button β€” that's how clicks reach your code.

function on_start()
  G = {}
  G["count"] = 0
  spawn_screen_gui("Demo")
  spawn_gui("panel", "frame",  "Demo", "",          40,  40, 260, 130, 0.10, 0.11, 0.18)
  spawn_gui("lbl",   "label",  "Demo", "Clicks: 0", 40,  60, 260,  28, 0.85, 0.90, 1.00)
  spawn_gui("hit",   "button", "Demo", "Click me",  90, 110, 160,  44, 0.36, 0.35, 1.00)
end

-- the button is named "hit", so its event is on_hit
function on_hit()
  G["count"] = G["count"] + 1
  set_text("lbl", "Clicks: " + str(G["count"]))
end

Element types: frame, label, button, textbox, imagelabel, imagebutton. Positions are in pixels (x, y, width, height), colors are three 0–1 numbers.

One handler for lots of buttons. Every click also fires on_click() and sets two values: clicked (the button's name) and clicked_num (its trailing digits as a number, or βˆ’1). So a whole grid needs one handler, not one per cell:

-- 64 buttons named c0 … c63, handled once
function on_click()
  if clicked_num >= 0 then
    tap(clicked_num)
  end
end

Common mistakes

1. One-line if … then … end β€” must be split across lines.
2. A function call inside a bigger expression β€” pull it onto its own line first.
3. A function trying to change a global number β€” use a table instead.
4. b[12] and b[12.0] are different table keys. Loop counters are whole numbers, so this usually just works.
5. A whole command must fit on one line β€” don't wrap the arguments.
6. Misspelled commands fail silently. If nothing happens, check the spelling and the output console.

All commands

Every command works both in Studio β–Ά Test and in a published world. A test: simulated tag means Test fakes it β€” because an editor has no network, no real purchases, and no other players β€” and prints what it did to the output console.

Showing all 67 commands.

No commands match that search.

Messages

CommandWhat it does
print(value, …)Writes to the output console.
show_toast("message")Short popup on screen.
chat_message("text")Posts a system message in chat.

This script's own part

CommandWhat it does
rotate(x, y, z)Spins it β€” degrees per second. Use in on_tick.
move(x, y, z)Slides it β€” units per second. Use in on_tick.
hover(amount, speed)Floats it up and down.
color(r, g, b)Recolors it.
scale(x, y, z)Resizes it.
delete()Removes it from the world.

Building

CommandWhat it does
spawn_part("Name", "Block", x, y, z, sx, sy, sz, r, g, b)Creates a part. Shapes: Block, Sphere, Cylinder, Cone, Wedge, Capsule, Torus, Plate.
spawn_light("Name", "point", x, y, z, r, g, b, energy, range)Creates a light β€” "point" or "spot".
set_position("Name", x, y, z)Moves a part.
set_size("Name", sx, sy, sz)Resizes a part.
set_rotation("Name", x, y, z)Rotates a part (degrees).
set_color("Name", r, g, b)Recolors a part.
set_material("Name", "neon")neon, wood, metal, plastic, glass…
set_anchored("Name", true)Anchored parts ignore gravity.
clone_part("Source", "NewName")Copies a part.
destroy("Name")Deletes a part.

Screen GUI

CommandWhat it does
spawn_screen_gui("ScreenName")Creates a screen. Do this before adding elements.
spawn_gui("Name", "type", "Screen", "text", x, y, w, h, r, g, b)Adds an element. A button fires on_<Name> when clicked.
set_text("Name", "text")Changes the words on a label or button.
set_gui_color("Name", r, g, b)Recolors an element.
set_visible("Name", true)Shows or hides an element.

Lists, values & events

CommandWhat it does
push(list, value)Adds to the end of a list.
pop(list)Removes the last item.
fire("event_name")Runs on_event_name() on every script.
set("key", value)Saves a Stored Value shared across the world.
random_int("key", min, max)Puts a random whole number into a Stored Value.

The player

CommandWhat it does
win()Triggers the win screen.
respawn()Sends the player back to spawn.
boost(seconds)Quick speed boost.
give_speed(multiplier, seconds)Timed speed multiplier.
teleport_player(x, y, z)Moves the player instantly.
kill_player("reason")Respawns them with a message.
take_damage(amount)Removes health.
heal(amount)Restores health.
set_walkspeed(value)How fast they walk.
set_jumppower(value)How high they jump.
play_animation("Name")Plays an avatar animation.
move_to(speed, "Waypoint")test: simulatedWalks this part to a spot. Test moves in a straight line (no obstacle routing).

Camera

CommandWhat it does
camera_shake(strength, seconds)Shakes the view β€” great for explosions.
camera_follow("PartName")Watches a part instead of the player.
camera_release()Gives the camera back to the player.
camera_first_person(true)Switches to first person.
set_camera_fov(degrees)Zooms the view in or out.

The world

CommandWhat it does
set_gravity(value)Changes gravity. 9.8 is normal, low = floaty.
set_time_of_day(0…24)Moves the sun. 12 is noon.
set_hinge_angle("Hinge", degrees)Opens or closes a hinge β€” doors, levers.
play_sound("SoundName")Plays a Sound you placed in the world.

Smooth motion (tweens)

CommandWhat it does
tween_position(x, y, z, seconds)Glides this part to a spot.
tween_rotation(x, y, z, seconds)Turns it smoothly.
tween_color(r, g, b, seconds)Fades its color.

Teams & leaderboard

CommandWhat it does
team_create("Name", r, g, b)Makes a team.
team_assign("Name")Puts the player on it.
leaderstat_set("Score", value)Sets a leaderboard number.
leaderstat_add("Score", 10)Adds to it.

Saving & internet

CommandWhat it does
data_set("key", value)test: simulatedSaves for that player between visits. In Test it only lasts the session.
data_get_fire("key", "event")test: simulatedLoads saved data, then fires your event so you can use it.
http_get_fire("url", "key", "event")test: simulatedFetches from an approved site. Test skips the request and fires with an empty result.

Gamepasses

CommandWhat it does
prompt_gamepass("pass_id")test: simulatedOpens the buy window. Test never charges β€” it just logs it.
if_owns_gamepass_fire("pass_id", "event")test: simulatedFires only for owners β€” VIP doors. Test assumes you own it so you can check the unlocked path.

Automatic triggers

CommandWhat it does
distance_fire("A", "B", range, "event")Fires when two parts get close.
player_count_fire(count, "event")test: simulatedFires when enough players join. Test always has 1 player.
for_count(n, "event")Fires an event n times.

Other

CommandWhat it does
require("Module", "function")test: simulatedCalls a function in a ModuleScript. Not run in Test.
wait(seconds)Pauses your script for that many seconds, then carries on β€” works inside loops and functions too. Great for countdowns, cooldowns and cutscenes. Max 60s.

What's new

Recent updates to Voxa Studio and the platform.

Latest

FeatureGamepasses

Create gamepasses in Studio. Players buy with VoxUs from a world's detail page, or via prompt_gamepass() popups your scripts trigger.

Latest

FeatureAchievements & badges

Nine achievements (Newcomer Β· Explorer Β· Socializer Β· Loaded …). Each gives 25–200 VoxUs when you claim.

Recent

FeatureSmarter chat filtering

Improved second-pass classifier for chat + comments. Catches stuff the static word-list misses.

Recent

FeatureBlock + Report

Block hides their chat and comments client-side. Report writes to a private queue for review.

Recent

FeatureWorld details page in-game

Full detail page when you click a world tile: votes, comments, gamepasses, info, all without leaving the menu.

Older

FeatureMove / Rotate / Scale gizmos

Q select Β· M move Β· R rotate Β· T scale. Drag the colored axis handles in the 3D viewport.

Older

FeatureTest mode runs scripts

Scripts and built-in behaviors (rotate, hover, kill, goal, speedpad) run when you hit β–Ά Test. Output console captures every print().

Older

NoteAuto-save every 60 s

Studio silently saves a draft to your account every minute.