Examples and Tutorials
🚀 Quick Examples
Hello World
Your first Vibe program - display text on screen.
lua
function on_init()
print("Starting Hello World!")
vibe.spawn("label", {
text = "Hello, Vibe API!",
x = 100,
y = 100,
tag = "greeting"
})
end
function on_tick(dt)
-- Nothing to update for static text
endBasic Player Movement
Move a sprite around with arrow keys.
lua
function on_init()
print("Player movement demo")
-- Spawn player in center of screen
local w, h = vibe.width(), vibe.height()
vibe.spawn("sprite", {
texture = "res://assets/player.png",
x = w / 2,
y = h / 2,
cell_size = 32,
tag = "player"
})
end
function on_tick(dt)
local speed = 200 -- pixels per second
-- Get input
local h = vibe.axis("horizontal") -- -1 to 1
local v = vibe.axis("vertical") -- -1 to 1
-- Move player
if h ~= 0 or v ~= 0 then
local players = vibe.find("player")
if #players > 0 then
local player = players[1]
local x = vibe.get(player, "x")
local y = vibe.get(player, "y")
-- Apply movement
vibe.set(player, "x", x + h * speed * dt)
vibe.set(player, "y", y + v * speed * dt)
-- Keep on screen
local w, h = vibe.width(), vibe.height()
x = math.max(0, math.min(x, w - 32))
y = math.max(0, math.min(y, h - 32))
vibe.set(player, "x", x)
vibe.set(player, "y", y)
end
end
endInteractive Objects
Click space near objects to interact.
lua
local interaction_range = 64
function on_init()
print("Interaction demo")
-- Spawn player
vibe.spawn("sprite", {
texture = "res://assets/player.png",
x = 100, y = 200,
cell_size = 32,
tag = "player"
})
-- Spawn some objects to interact with
for i = 1, 3 do
vibe.spawn("sprite", {
texture = "res://assets/chest.png",
x = 200 + i * 100,
y = 200,
cell_size = 32,
tag = "chest"
})
end
-- UI instructions
vibe.spawn("label", {
text = "Use arrow keys to move, SPACE to interact",
x = 10, y = 10,
tag = "ui"
})
end
function on_tick(dt)
-- Player movement (same as previous example)
update_player_movement(dt)
-- Check for interaction
if vibe.key("space") then
check_interactions()
end
end
function update_player_movement(dt)
local speed = 200
local h = vibe.axis("horizontal")
local v = vibe.axis("vertical")
local players = vibe.find("player")
if #players > 0 then
local player = players[1]
local x = vibe.get(player, "x")
local y = vibe.get(player, "y")
vibe.set(player, "x", x + h * speed * dt)
vibe.set(player, "y", y + v * speed * dt)
end
end
function check_interactions()
local players = vibe.find("player")
local chests = vibe.find("chest")
if #players == 0 then return end
local player = players[1]
local px = vibe.get(player, "x")
local py = vibe.get(player, "y")
for i = 1, #chests do
local chest = chests[i]
local cx = vibe.get(chest, "x")
local cy = vibe.get(chest, "y")
local distance = math.sqrt((px - cx)^2 + (py - cy)^2)
if distance < interaction_range then
-- Interact with chest
vibe.play_sfx("res://assets/open_chest.wav")
vibe.destroy(chest)
-- Spawn reward
vibe.spawn("sprite", {
texture = "res://assets/coin.png",
x = cx, y = cy,
cell_size = 24,
tag = "reward"
})
print("Chest opened!")
break -- Only open one chest per press
end
end
end🎮 Complete Games
Game 1: Coin Collection
A complete game where you collect coins and avoid enemies.
lua
-- Game state
local score = 0
local lives = 3
local coin_spawn_timer = 0
local enemy_spawn_timer = 0
local game_over = false
function on_init()
print("Starting Coin Collection Game!")
local w, h = vibe.width(), vibe.height()
-- Spawn player
vibe.spawn("sprite", {
texture = "res://assets/player.png",
x = w / 2,
y = h / 2,
cell_size = 32,
tag = "player"
})
-- Create UI
vibe.spawn("label", {
text = "Score: 0",
x = 10, y = 10,
tag = "score_ui"
})
vibe.spawn("label", {
text = "Lives: 3",
x = 10, y = 30,
tag = "lives_ui"
})
vibe.spawn("label", {
text = "Collect coins, avoid enemies!",
x = 10, y = h - 30,
tag = "instructions"
})
-- Spawn initial coins and enemies
for i = 1, 3 do
spawn_coin()
spawn_enemy()
end
end
function on_tick(dt)
if game_over then
handle_game_over()
return
end
update_player(dt)
update_spawning(dt)
check_collisions()
update_ui()
end
function update_player(dt)
local speed = 200
local h = vibe.axis("horizontal")
local v = vibe.axis("vertical")
local players = vibe.find("player")
if #players > 0 then
local player = players[1]
local x = vibe.get(player, "x")
local y = vibe.get(player, "y")
-- Move player
local new_x = x + h * speed * dt
local new_y = y + v * speed * dt
-- Keep on screen
local w, h = vibe.width(), vibe.height()
new_x = math.max(16, math.min(new_x, w - 48))
new_y = math.max(16, math.min(new_y, h - 48))
vibe.set(player, "x", new_x)
vibe.set(player, "y", new_y)
end
end
function update_spawning(dt)
coin_spawn_timer = coin_spawn_timer + dt
enemy_spawn_timer = enemy_spawn_timer + dt
-- Spawn coins every 2 seconds
if coin_spawn_timer >= 2.0 then
spawn_coin()
coin_spawn_timer = 0
end
-- Spawn enemies every 4 seconds (gets faster)
local enemy_interval = math.max(1.0, 4.0 - score * 0.1)
if enemy_spawn_timer >= enemy_interval then
spawn_enemy()
enemy_spawn_timer = 0
end
end
function spawn_coin()
local w, h = vibe.width(), vibe.height()
vibe.spawn("sprite", {
texture = "res://assets/coin.png",
x = math.random(32, w - 64),
y = math.random(32, h - 64),
cell_size = 24,
tag = "coin"
})
end
function spawn_enemy()
local w, h = vibe.width(), vibe.height()
local side = math.random(1, 4)
local x, y
-- Spawn from edges
if side == 1 then -- Top
x, y = math.random(0, w), -32
elseif side == 2 then -- Right
x, y = w + 32, math.random(0, h)
elseif side == 3 then -- Bottom
x, y = math.random(0, w), h + 32
else -- Left
x, y = -32, math.random(0, h)
end
vibe.spawn("sprite", {
texture = "res://assets/enemy.png",
x = x, y = y,
cell_size = 28,
tag = "enemy"
})
end
function check_collisions()
local players = vibe.find("player")
if #players == 0 then return end
local player = players[1]
local px = vibe.get(player, "x")
local py = vibe.get(player, "y")
-- Check coin collection
local coins = vibe.find("coin")
for i = 1, #coins do
local coin = coins[i]
local cx = vibe.get(coin, "x")
local cy = vibe.get(coin, "y")
if math.abs(px - cx) < 28 and math.abs(py - cy) < 28 then
vibe.play_sfx("res://assets/collect.wav")
vibe.destroy(coin)
score = score + 10
end
end
-- Check enemy collision
local enemies = vibe.find("enemy")
for i = 1, #enemies do
local enemy = enemies[i]
local ex = vibe.get(enemy, "x")
local ey = vibe.get(enemy, "y")
if math.abs(px - ex) < 30 and math.abs(py - ey) < 30 then
vibe.play_sfx("res://assets/hurt.wav")
vibe.destroy(enemy)
lives = lives - 1
if lives <= 0 then
game_over = true
end
end
end
end
function update_ui()
local score_labels = vibe.find("score_ui")
if #score_labels > 0 then
vibe.set(score_labels[1], "text", "Score: " .. score)
end
local lives_labels = vibe.find("lives_ui")
if #lives_labels > 0 then
vibe.set(lives_labels[1], "text", "Lives: " .. lives)
end
end
function handle_game_over()
-- Update instructions to show final score
local instructions = vibe.find("instructions")
if #instructions > 0 then
vibe.set(instructions[1], "text", "GAME OVER! Final Score: " .. score)
end
-- Stop enemy movement (they'll just stay where they are)
-- In a more complex game, you might clear all entities and show a restart option
endGame 2: Snake
Classic Snake game with growing segments.
lua
-- Game state
local snake_segments = {}
local food_id = 0
local direction = {x = 1, y = 0}
local next_direction = {x = 1, y = 0}
local move_timer = 0
local move_interval = 0.25
local grid_size = 24
local score = 0
local game_over = false
function on_init()
print("Starting Snake Game!")
local w, h = vibe.width(), vibe.height()
-- Create initial snake (3 segments)
local start_x = math.floor(w / 2 / grid_size) * grid_size
local start_y = math.floor(h / 2 / grid_size) * grid_size
for i = 0, 2 do
local texture = i == 0 and "res://assets/snake_head.png" or "res://assets/snake_body.png"
local segment = vibe.spawn("sprite", {
texture = texture,
x = start_x - i * grid_size,
y = start_y,
cell_size = grid_size,
tag = "snake"
})
table.insert(snake_segments, segment)
end
-- Spawn first food
spawn_food()
-- Create UI
vibe.spawn("label", {
text = "Score: 0",
x = 10, y = 10,
tag = "score"
})
vibe.spawn("label", {
text = "Arrow Keys to Move",
x = 10, y = h - 50,
tag = "instructions"
})
vibe.spawn("label", {
text = "Eat the food, avoid yourself!",
x = 10, y = h - 30,
tag = "hint"
})
end
function on_tick(dt)
if game_over then
handle_game_over()
return
end
handle_input()
update_snake(dt)
check_food_collision()
check_self_collision()
update_ui()
end
function handle_input()
-- Prevent reverse direction
if vibe.key("up") and direction.y ~= 1 then
next_direction = {x = 0, y = -1}
elseif vibe.key("down") and direction.y ~= -1 then
next_direction = {x = 0, y = 1}
elseif vibe.key("left") and direction.x ~= 1 then
next_direction = {x = -1, y = 0}
elseif vibe.key("right") and direction.x ~= -1 then
next_direction = {x = 1, y = 0}
end
end
function update_snake(dt)
move_timer = move_timer + dt
if move_timer >= move_interval then
move_timer = 0
direction = next_direction -- Apply direction change
-- Get current head position
local head = snake_segments[1]
local head_x = vibe.get(head, "x")
local head_y = vibe.get(head, "y")
-- Calculate new head position
local new_x = head_x + direction.x * grid_size
local new_y = head_y + direction.y * grid_size
-- Check wall collision
local w, h = vibe.width(), vibe.height()
if new_x < 0 or new_x >= w or new_y < 0 or new_y >= h then
game_over = true
vibe.play_sfx("res://assets/game_over.wav")
return
end
-- Move body segments (from tail to head)
for i = #snake_segments, 2, -1 do
local prev_x = vibe.get(snake_segments[i-1], "x")
local prev_y = vibe.get(snake_segments[i-1], "y")
vibe.set(snake_segments[i], "x", prev_x)
vibe.set(snake_segments[i], "y", prev_y)
end
-- Move head
vibe.set(head, "x", new_x)
vibe.set(head, "y", new_y)
end
end
function check_food_collision()
if food_id == 0 then return end
local head = snake_segments[1]
local head_x = vibe.get(head, "x")
local head_y = vibe.get(head, "y")
local food_x = vibe.get(food_id, "x")
local food_y = vibe.get(food_id, "y")
if head_x == food_x and head_y == food_y then
-- Eat food
vibe.play_sfx("res://assets/eat.wav")
vibe.destroy(food_id)
food_id = 0
grow_snake()
spawn_food()
score = score + 10
-- Speed up slightly
move_interval = math.max(0.1, move_interval * 0.95)
end
end
function grow_snake()
-- Add new segment at current tail position
local tail = snake_segments[#snake_segments]
local tail_x = vibe.get(tail, "x")
local tail_y = vibe.get(tail, "y")
local new_segment = vibe.spawn("sprite", {
texture = "res://assets/snake_body.png",
x = tail_x,
y = tail_y,
cell_size = grid_size,
tag = "snake"
})
table.insert(snake_segments, new_segment)
end
function spawn_food()
local w, h = vibe.width(), vibe.height()
local grid_w = math.floor(w / grid_size)
local grid_h = math.floor(h / grid_size)
-- Find empty position (try up to 100 times)
for attempt = 1, 100 do
local fx = math.random(0, grid_w - 1) * grid_size
local fy = math.random(0, grid_h - 1) * grid_size
local occupied = false
-- Check if position is occupied by snake
for i = 1, #snake_segments do
local sx = vibe.get(snake_segments[i], "x")
local sy = vibe.get(snake_segments[i], "y")
if fx == sx and fy == sy then
occupied = true
break
end
end
if not occupied then
food_id = vibe.spawn("sprite", {
texture = "res://assets/food.png",
x = fx,
y = fy,
cell_size = grid_size,
tag = "food"
})
break
end
end
end
function check_self_collision()
local head = snake_segments[1]
local head_x = vibe.get(head, "x")
local head_y = vibe.get(head, "y")
-- Check collision with body segments
for i = 2, #snake_segments do
local seg_x = vibe.get(snake_segments[i], "x")
local seg_y = vibe.get(snake_segments[i], "y")
if head_x == seg_x and head_y == seg_y then
game_over = true
vibe.play_sfx("res://assets/game_over.wav")
break
end
end
end
function update_ui()
local score_labels = vibe.find("score")
if #score_labels > 0 then
vibe.set(score_labels[1], "text", "Score: " .. score .. " (Length: " .. #snake_segments .. ")")
end
end
function handle_game_over()
local instructions = vibe.find("instructions")
if #instructions > 0 then
vibe.set(instructions[1], "text", "GAME OVER!")
end
local hints = vibe.find("hint")
if #hints > 0 then
vibe.set(hints[1], "text", "Final Score: " .. score .. " (Length: " .. #snake_segments .. ")")
end
end📚 Step-by-Step Tutorial: Building Your First Game
Let's build a simple "Dodge the Asteroids" game from scratch.
Step 1: Basic Setup
lua
function on_init()
print("Starting Asteroid Dodge!")
-- We'll add more here as we go
end
function on_tick(dt)
-- Game logic will go here
endStep 2: Add Player
lua
function on_init()
print("Starting Asteroid Dodge!")
-- Spawn player at bottom center
local w = vibe.width()
vibe.spawn("sprite", {
texture = "res://assets/player.png",
x = w / 2 - 16,
y = vibe.height() - 64,
cell_size = 32,
tag = "player"
})
end
function on_tick(dt)
-- Move player left/right only
local speed = 300
local h = vibe.axis("horizontal")
local players = vibe.find("player")
if #players > 0 then
local player = players[1]
local x = vibe.get(player, "x")
x = x + h * speed * dt
x = math.max(0, math.min(x, vibe.width() - 32))
vibe.set(player, "x", x)
end
endStep 3: Add Asteroids
lua
-- Add at the top of your script
local asteroid_spawn_timer = 0
function on_init()
print("Starting Asteroid Dodge!")
-- Player setup (same as before)
local w = vibe.width()
vibe.spawn("sprite", {
texture = "res://assets/player.png",
x = w / 2 - 16,
y = vibe.height() - 64,
cell_size = 32,
tag = "player"
})
end
function on_tick(dt)
-- Player movement (same as before)
update_player(dt)
-- Spawn asteroids
update_asteroid_spawning(dt)
-- Move asteroids down
update_asteroids(dt)
end
function update_player(dt)
local speed = 300
local h = vibe.axis("horizontal")
local players = vibe.find("player")
if #players > 0 then
local player = players[1]
local x = vibe.get(player, "x")
x = x + h * speed * dt
x = math.max(0, math.min(x, vibe.width() - 32))
vibe.set(player, "x", x)
end
end
function update_asteroid_spawning(dt)
asteroid_spawn_timer = asteroid_spawn_timer + dt
if asteroid_spawn_timer >= 1.0 then -- Every second
spawn_asteroid()
asteroid_spawn_timer = 0
end
end
function spawn_asteroid()
local w = vibe.width()
vibe.spawn("sprite", {
texture = "res://assets/asteroid.png",
x = math.random(0, w - 32),
y = -32,
cell_size = 32,
tag = "asteroid"
})
end
function update_asteroids(dt)
local speed = 150
local asteroids = vibe.find("asteroid")
for i = 1, #asteroids do
local asteroid = asteroids[i]
local y = vibe.get(asteroid, "y")
y = y + speed * dt
vibe.set(asteroid, "y", y)
-- Remove asteroids that go off screen
if y > vibe.height() + 32 then
vibe.destroy(asteroid)
end
end
endStep 4: Add Collision Detection
lua
-- Add these variables at the top
local asteroid_spawn_timer = 0
local lives = 3
local invulnerable_timer = 0
function on_init()
print("Starting Asteroid Dodge!")
-- Player setup
local w = vibe.width()
vibe.spawn("sprite", {
texture = "res://assets/player.png",
x = w / 2 - 16,
y = vibe.height() - 64,
cell_size = 32,
tag = "player"
})
-- UI
vibe.spawn("label", {
text = "Lives: 3",
x = 10, y = 10,
tag = "lives_ui"
})
end
function on_tick(dt)
-- Update invulnerability
if invulnerable_timer > 0 then
invulnerable_timer = invulnerable_timer - dt
end
update_player(dt)
update_asteroid_spawning(dt)
update_asteroids(dt)
check_collisions()
update_ui()
end
function check_collisions()
if invulnerable_timer > 0 then return end -- Still invulnerable
local players = vibe.find("player")
local asteroids = vibe.find("asteroid")
if #players == 0 then return end
local player = players[1]
local px = vibe.get(player, "x")
local py = vibe.get(player, "y")
for i = 1, #asteroids do
local asteroid = asteroids[i]
local ax = vibe.get(asteroid, "x")
local ay = vibe.get(asteroid, "y")
if math.abs(px - ax) < 28 and math.abs(py - ay) < 28 then
-- Collision!
vibe.play_sfx("res://assets/hit.wav")
vibe.destroy(asteroid)
lives = lives - 1
invulnerable_timer = 2.0 -- 2 seconds of invulnerability
if lives <= 0 then
game_over()
end
break
end
end
end
function update_ui()
local lives_labels = vibe.find("lives_ui")
if #lives_labels > 0 then
local text = "Lives: " .. lives
if invulnerable_timer > 0 then
text = text .. " (INVULNERABLE)"
end
vibe.set(lives_labels[1], "text", text)
end
end
function game_over()
print("Game Over!")
-- Clear all asteroids
local asteroids = vibe.find("asteroid")
for i = 1, #asteroids do
vibe.destroy(asteroids[i])
end
-- Show game over message
vibe.spawn("label", {
text = "GAME OVER!",
x = vibe.width() / 2 - 50,
y = vibe.height() / 2,
tag = "game_over"
})
end
-- (Include all the previous functions: update_player, update_asteroid_spawning, etc.)Step 5: Polish and Extend
Add these features to make your game more engaging:
- Scoring system: Award points for surviving time
- Power-ups: Temporary shields, speed boosts
- Different asteroid types: Various sizes and speeds
- Particle effects: Explosions, trail effects
- Better audio: Background music, varied sound effects
- Difficulty scaling: Faster/more asteroids over time
🎯 Learning Challenges
Try building these games to practice different concepts:
Beginner Projects
- Clicker Game: Click to spawn entities, count clicks
- Color Match: Match falling colored blocks
- Simple Maze: Navigate through a static maze
- Memory Game: Simon-says style sequence matching
Intermediate Projects
- Tower Defense: Enemies follow paths, towers shoot
- Platformer: Jumping, gravity, simple collision
- Top-down Shooter: 360-degree shooting, enemy waves
- Puzzle Game: Tetris or match-3 mechanics
Advanced Projects
- Real-time Strategy: Multiple unit types, resource management
- RPG Systems: Stats, inventory, turn-based combat
- Physics Simulation: Bouncing balls, gravity wells
- Procedural Generation: Random levels, infinite worlds
Each project will teach you different aspects of game development with the Vibe API!
Ready for more advanced techniques? Check out Advanced Topics for performance optimization, design patterns, and professional development practices.