Lua Documentation

Codetoy supports Lua 5.4 out of the box. The plan is to switch to Luau entirely or at least support luau powered linting as soon as possible, but for now Lua 5.4 is what is supported.

Canvas

State Stack

reset()

push()

pop()

Color State

fill(r, g, b, a = 1.0)

stroke(r, g, b, a = 1.0)

Line State

lineWidth(width: number)

lineJoin("round" | "bevel" | "miter")

lineMiterLimit(limit: number)

lineCap("butt" | "round" | "square")

Shape Drawing

rect(x, y, w, h, r = 0)

circle(x, y, radius)

line(x1, y1, x2, y2)

ellipse(x, y, w, h)

triangle(x1, y1, x2, y2, x3, y3)

polygon(number[] points) (eg. x1,y1, x2,y2, ...)

Transform

scale(x, y)

rotate(radians)

translate(x, y)

resetTransform()

Advanced Drawing

beginPath()

moveTo(x, y)

lineTo(x, y)

bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)

quadraticCurveTo(cpx, cpy, x, y)

closePath()

fillPath()

strokePath()

Text

font(family, size, weight = "normal")

text(text, x, y)

measureText(text) → number (returns width of text)

Input

Mouse

mouseX and mouseY → number

isMouseDown(button: number) → bool (0=left, 1=middle, 2=right)

function onMouseDown(key: string) if a global function with this signature exists it will be called

function onMouseUp(key: string) if a global function with this signature exists it will be called

Keyboard

isKeyDown(key: string) → bool (if the key is being held down)

function onKeyDown(key: string) if a global function with this signature exists it will be called

function onKeyUp(key: string) if a global function with this signature exists it will be called

Screen

width and height → number

centerX and centerY → number

Console

print(message: string)

API Examples

fill(r, g, b, a?)

Sets the fill color for subsequent drawing operations. Colors use RGBA format (0-255 for RGB, 0-1 for alpha).

Parameters:

  • r (number): Red channel (0-255)
  • g (number): Green channel (0-255)
  • b (number): Blue channel (0-255)
  • a (number, optional): Alpha/opacity (0-1, default: 1.0)

Example:

function update()
  fill(255, 0, 0, 0.8)  -- Semi-transparent red
  rect(50, 50, 100, 100)
end

rect(x, y, width, height, radius?)

Draws a rectangle at the specified position with optional rounded corners.

Parameters:

  • x (number): X coordinate of top-left corner
  • y (number): Y coordinate of top-left corner
  • width (number): Width of rectangle
  • height (number): Height of rectangle
  • radius (number, optional): Corner radius for rounding (default: 0)

Example:

function update()
  fill(0, 150, 255)
  rect(100, 100, 200, 150, 10)  -- Blue rounded rectangle
end

circle(x, y, radius)

Draws a circle at the specified position.

Parameters:

  • x (number): X coordinate of circle center
  • y (number): Y coordinate of circle center
  • radius (number): Circle radius

Example:

function update()
  fill(100, 255, 100)
  circle(width / 2, height / 2, 50)  -- Green circle in center
end

ellipse(x, y, width, height)

Draws an ellipse at the specified position.

Parameters:

  • x (number): X coordinate of ellipse center
  • y (number): Y coordinate of ellipse center
  • width (number): Horizontal radius (half-width)
  • height (number): Vertical radius (half-height)

Example:

function update()
  fill(255, 200, 0)
  ellipse(width / 2, height / 2, 80, 40)  -- Yellow ellipse
end

Global Variables

width

The current canvas width in pixels. Updates when the window is resized.

height

The current canvas height in pixels. Updates when the window is resized.

mouseX

The current X coordinate of the mouse in canvas space. Updated on mouse movement.

mouseY

The current Y coordinate of the mouse in canvas space. Updated on mouse movement.

deltaTime

The time elapsed since the last frame in seconds. Useful for smooth animations.

Example:

local speed = 100  -- pixels per second
local x = 0

function update()
  x = x + speed * deltaTime
  fill(255, 100, 200)
  rect(x, 100, 50, 50)
end

Event Functions

update(deltaTime)

Called every frame. Use this for animations and game logic.

Parameters:

  • deltaTime (number): Time elapsed since last frame in seconds

Example:

function update()
  fill(255, 100, 0)
  circle(mouseX, mouseY, 20)  -- Follow cursor
end

mouseDown(button)

Called when a mouse button is pressed.

Parameters:

  • button (number): 0 = left, 1 = middle, 2 = right

Example:

function mouseDown(button)
  if button == 0 then
    print("Left click at: " .. mouseX .. ", " .. mouseY)
  end
end

mouseUp(button)

Called when a mouse button is released.

Parameters:

  • button (number): 0 = left, 1 = middle, 2 = right

resize()

Called when the canvas/window is resized.

Example:

function resize()
  print("Canvas resized to: " .. width .. "x" .. height)
end

Math

The math library provides mathematical operations and trigonometric functions.

math.sin(x)

Returns the sine of x (in radians).

Example:

local xPos = 0

function update()
  local halfHeight = height * 0.5

  fill(255, 0, 0, 1)
  rect(
    xPos,
    halfHeight + (halfHeight * math.sin(xPos / 10)), 
    10, 
    10
  )

  fill(0, 255, 255, 1)
  rect(mouseX, mouseY, 10, 10)

  xPos = xPos + (deltaTime * 50)
end

math.cos(x)

Returns the cosine of x (in radians).

Example:

local time = 0

function update()
  fill(20, 20, 30)
  rect(0, 0, width, height)

  time = time + deltaTime
  
  local x = width / 2 + math.cos(time) * 100
  local y = height / 2 + math.sin(time) * 100
  
  fill(100, 200, 255)
  circle(x, y, 20)
end

math.abs(x)

Returns the absolute value of x.

Example:

function update()
  local distX = math.abs(mouseX - width / 2)
  local distY = math.abs(mouseY - height / 2)
  
  fill(distX / 2, distY / 2, 200)
  rect(0, 0, width, height)
  
  fill(255, 100, 100)
  rect(width / 2 - 50, height / 2 - 50, 100, 100)
end

math.sqrt(x)

Returns the square root of x.

Example:

function update()
  fill(30, 30, 30)
  rect(0, 0, width, height)
  
  local distX = mouseX - width / 2
  local distY = mouseY - height / 2
  local distance = math.sqrt(distX * distX + distY * distY)
  local radius = math.max(1, distance / 5)
  
  fill(100 + (distance / 2) % 155, 150, 200)
  circle(width / 2, height / 2, radius)
end

math.max(...)

Returns the largest number from the arguments provided.

Example:

function update()
  local size = math.max(50, width / 4, height / 4)
  
  fill(100, 200, 255)
  rect(width / 2 - size / 2, height / 2 - size / 2, size, size)
end

math.min(...)

Returns the smallest number from the arguments provided.

Example:

function update()
  fill(40, 40, 40)
  rect(0, 0, width, height)
  
  local maxRadius = math.min(width, height) / 4
  local radiusPulse = maxRadius * (0.5 + 0.5 * math.sin(deltaTime))
  
  fill(255, 100, 100)
  circle(width / 2, height / 2, radiusPulse)
end

math.random()

Returns a random number between 0 and 1.

Example:

function update()
  fill(30, 30, 30)
  rect(0, 0, width, height)
  
  for i = 1, 20 do
    local x = math.random() * width
    local y = math.random() * height
    local r = math.random() * 255
    local g = math.random() * 255
    local b = math.random() * 255
    
    fill(r, g, b)
    circle(x, y, 5)
  end
end

math.floor(x)

Returns x rounded down to the nearest integer.

Example:

function update()
  fill(40, 40, 40)
  rect(0, 0, width, height)
  
  local gridSize = 40
  local col = math.floor(mouseX / gridSize)
  local row = math.floor(mouseY / gridSize)
  
  fill(100, 200, 255)
  rect(col * gridSize, row * gridSize, gridSize, gridSize)
end

math.floor(x) (alternative: rounding)

For rounding to nearest integer, use math.floor(x + 0.5).

Example:

function update()
  fill(40, 40, 40)
  rect(0, 0, width, height)

  local gridSize = 40
  local col = math.floor(mouseX / gridSize + 0.5) * gridSize
  local row = math.floor(mouseY / gridSize + 0.5) * gridSize

  local halfGridSize = 20
  fill(100, 200, 255)
  rect(col - halfGridSize, row - halfGridSize, gridSize, gridSize)
end

math.ceil(x)

Returns x rounded up to the nearest integer.

Example:

function update()
  fill(40, 40, 40)
  rect(0, 0, width, height)
  
  local value = (mouseY / height) * 10
  local ceiled = math.ceil(value)
  local barHeight = ceiled * 20
  
  fill(255, 100, 100)
  rect(width / 2 - 50, height - barHeight, 100, barHeight)
end

math.pow(x, y) or ^ operator

Returns x raised to the power of y (x^y).

Example:

function update()
  fill(20, 20, 30)
  rect(0, 0, width, height)
  
  local normalizedX = mouseX / width
  local radius = math.pow(normalizedX, 2) * 150
  
  fill(100, 200, 255)
  circle(width / 2, height / 2, radius)
end

math.pi

The constant π (approximately 3.14159).

Example:

local time = 0

function update()
  fill(30, 30, 30)
  rect(0, 0, width, height)
  
  time = time + deltaTime
  
  for i = 0, 5 do
    local angle = ((i / 6) * 2 * math.pi) + time
    local x = width / 2 + math.cos(angle) * 100
    local y = height / 2 + math.sin(angle) * 100
    
    fill(100 + i * 30, 150, 200)
    circle(x, y, 15)
  end
end

math.atan2(y, x)

Returns the arctangent of y/x in radians, useful for calculating angles.

Example:

function update()
  fill(40, 40, 40)
  rect(0, 0, width, height)
  
  local centerX = width / 2
  local centerY = height / 2
  local angle = math.atan2(mouseY - centerY, mouseX - centerX)
  
  fill(255, 100, 100)
  rect(centerX - 2, centerY - 2, 4, 4)
  
  local endX = centerX + math.cos(angle) * 100
  local endY = centerY + math.sin(angle) * 100
  
  fill(100, 200, 255)
  circle(endX, endY, 10)
end

Example Programs

Example 1: Interactive Circle Following Cursor

function update()
  fill(200, 200, 200)
  rect(0, 0, width, height)  -- Clear background
  
  fill(0, 150, 255)
  circle(mouseX, mouseY, 30)  -- Blue circle follows mouse
end

function mouseDown(button)
  print("Clicked at " .. mouseX .. ", " .. mouseY)
end

Example 2: Animated Rectangle

local x = 0
local speed = 150  -- pixels per second

function update()
  -- Clear background
  fill(50, 50, 50)
  rect(0, 0, width, height)
  
  -- Bounce rectangle
  x = x + speed * deltaTime
  if x + 100 > width or x < 0 then
    speed = -speed
  end
  
  fill(100, 200, 255)
  rect(x, height / 2 - 50, 100, 100)
end

Example 3: Responsive Grid on Resize

local cols = 5
local rows = 4

function update()
  fill(30, 30, 30)
  rect(0, 0, width, height)  -- Background
  
  local cellWidth = width / cols
  local cellHeight = height / rows
  
  for row = 0, rows - 1 do
    for col = 0, cols - 1 do
      fill(100 + col * 30, 100 + row * 30, 200)
      rect(col * cellWidth, row * cellHeight, cellWidth, cellHeight, 5)
    end
  end
end

function resize()
  print("Resized to " .. width .. "x" .. height)
end

Example 4: Drawing with Mouse

local points = {}

function update()
  fill(40, 40, 40)
  rect(0, 0, width, height)  -- Clear background
  
  -- Draw all recorded points
  fill(255, 100, 100)
  for i, point in ipairs(points) do
    circle(point.x, point.y, 5)
  end
end

function mouseDown(button)
  if button == 0 then
    table.insert(points, { x = mouseX, y = mouseY })
  end
end

function resize()
  points = {}  -- Clear points on resize
end

Example 5: Pulsing Ellipses

local time = 0

function update()
  fill(20, 20, 30)
  rect(0, 0, width, height)  -- Background
  
  time = time + deltaTime
  
  for i = 1, 5 do
    local delay = i * 0.2
    local scale = 1 + math.sin(time * 3 - delay) * 0.5
    
    fill(100 + i * 30, 150, 200)
    ellipse(width / 2, height / 2, 40 * scale, 20 * scale)
  end
end

Want to learn more?

Join the discord and chat with the community