-
Notifications
You must be signed in to change notification settings - Fork 5
Lua Scripting API
Note
Note: This document was AI-generated via claude sonnet 4.6 and is a work in progress. Things may change in the future.
A LuaJIT-powered scripting layer for Funkin' View that allows chart creators to hook into gameplay events, create custom visual elements, and manipulate the playfield at runtime.
The system is composed of three main classes:
| Class | Responsibility |
|---|---|
FunkinViewLua |
Top-level manager. Loads .lua files, routes function calls to all active VM instances. |
FunkinViewLuaScript |
Wraps a single Lua VM (llua.State). Handles callback registration. |
CustomLuaSpriteComponent |
Exposes sprite/text/program/buffer APIs to Lua scripts. |
Scripts are loaded from the chart's directory and, optionally, a stage-specific .lua file at stages/<stageName>.lua.
These are Lua functions your script can define. They are called automatically by the engine at the appropriate time.
| Function | Arguments | Notes |
|---|---|---|
create() |
— | Called once on script load, before createPost. |
createPost() |
— | Called after the playfield finishes initializing. |
update(deltaTime) |
deltaTime: Float |
Called every frame before gameplay updates. |
updatePost(deltaTime) |
deltaTime: Float |
Called every frame after gameplay updates. Also available as postUpdate. |
render() |
— | Called every render frame. |
renderPost() |
— | Called after the render pass. Also available as postRender. |
dispose() |
— | Called when the playfield is being torn down. |
disposePost() |
— | Called after disposal. Also available as postDispose. |
| Function | Arguments |
|---|---|
startSong(title, difficulty) |
title: String, difficulty: String
|
startSongPost(title, difficulty) |
Also available as postStartSong
|
stopSong(title, difficulty) |
title: String, difficulty: String
|
stopSongPost(title, difficulty) |
Also available as postStopSong
|
| Function | Arguments |
|---|---|
stepHit(step) |
step: Float |
beatHit(beat) |
beat: Float |
measureHit(measure) |
measure: Float |
| Function | Arguments |
|---|---|
hitNote(pos, index, duration, type, timing, notesInOne) |
|
hitNotePost(...) |
Also available as postHitNote
|
missNote(pos, index, duration, type, notesInOne) |
|
missNotePost(...) |
Also available as postMissNote
|
completeSustain(pos, index, duration, type) |
|
completeSustainPost(...) |
Also available as postCompleteSustain
|
releaseSustain(pos, index, duration, type) |
|
releaseSustainPost(...) |
Also available as postReleaseSustain
|
| Function | Arguments |
|---|---|
pause() |
— |
pausePost() |
Also available as postPause
|
resume() |
— |
resumePost() |
Also available as postResume
|
| Function | Arguments |
|---|---|
gameOver() |
— |
gameOverPost() |
Also available as postGameOver
|
All functions below are callable from within any Lua script.
trace(message)
-- Prints a message to the console.Buffers hold collections of LuaSprite elements for batch rendering.
customBufferNew(bufferName, minSize, growSize, autoShrink)
-- Creates a new buffer.
-- bufferName: String (must not be empty)
-- minSize: Int
-- growSize: Int (default: 0)
-- autoShrink: Bool (default: false)
updateBuffer(bufferName)
-- Flushes and resubmits the buffer's data to the GPU.A program links a buffer to a texture for rendering.
customProgramNew(programName, customBuffer)
-- Creates a new render program attached to an existing buffer.
addTextureToProgram(programName, texturePNG, disableAntialiasing)
-- Loads a PNG from the assets folder and binds it to the program.
-- texturePNG: String (relative asset path)
-- disableAntialiasing: Bool (default: false)
wipeTextureFromProgram(programName)
-- Unbinds and removes the texture from the program.
addProgramToDisplay(programName, toDisplay, isBehind, atCustomProgram)
-- Adds the program to a named display (e.g. "display", "view", "roof").
-- isBehind: Bool (default: false) — render behind existing programs
-- atCustomProgram: String (optional) — insert relative to another program
removeProgramFromDisplay(programName, fromDisplay)
-- Removes the program from the specified display.customElementNew(elem, x, y, w, h, color)
-- Creates a new sprite element.
-- color: String (color name or 0xAARRGGBB hex, default: "white")
addElementToBuffer(elemName, bufferName)
-- Adds the element to a buffer for rendering.
removeElementFromBuffer(elemName, bufferName)
-- Removes the element from a buffer.
updateElementToBuffer(elemName, bufferName)
-- Marks the element as dirty so the buffer re-uploads it.
-- Position
setElementPos(elemName, x, y)
getElementPosX(elemName) --> Float
getElementPosY(elemName) --> Float
-- Size (UV coordinates)
setElementCoordinate(elemName, w, h)
getElementCoordinateX(elemName) --> Float
getElementCoordinateY(elemName) --> Float
-- Color
setElementTint(elemName, color)
getElementTint(elemName) --> String -- returns "0xAARRGGBB"
-- Alpha (0.0 – 1.0)
setElementAlpha(elemName, alpha)
getElementAlpha(elemName) --> Float
-- Rotation (degrees)
setElementAngle(elemName, rotation)
getElementAngle(elemName) --> Float
-- Centering
screenCenterElement(elemName, fromDisplay, axis)
-- axis: "X", "Y", or "XY"Text elements automatically bind to a live gameplay value (e.g. score, combo).
customTextNew(textElem, x, y, toDisplay, text, font, color, outlineSize, outlineColor)
-- toDisplay: String — a PlayField field name (e.g. "score", "combo", "accuracy")
-- font: String (default: "vcr")
-- color: String (default: "white")
-- outlineSize: Int (default: 0)
-- outlineColor: String (default: "black")
-- Position
setTextPos(textElem, x, y)
getTextPosX(textElem) --> Float
getTextPosY(textElem) --> Float
-- Content
setTextString(textElem, text)
getTextString(textElem) --> String
-- Color
setTextColor(textElem, color)
getTextColor(textElem) --> String
setTextOutlineColor(textElem, color)
getTextOutlineColor(textElem) --> String
setTextOutlineSize(textElem, size)
getTextOutlineSize(textElem) --> Float
-- Centering
screenCenterText(elemName, axis)
-- axis: "X", "Y", or "XY"
-- Visibility
hideText(elemName)
showText(elemName)setDisplayAngle(fromDisplay, rotation)
-- Rotates a named display (e.g. "display", "view").
getTextureCoordinateX(programName) --> Int -- texture pixel width
getTextureCoordinateY(programName) --> Int -- texture pixel heightColors can be passed as:
-
Named colors — e.g.
"white","black","red"(resolved viaColor.defaultMap) -
Hex strings — e.g.
"0xFFFFFFFF"(AARRGGBB format)
local mySprite = "box"
local myBuf = "myBuffer"
local myProg = "myProgram"
function create()
customBufferNew(myBuf, 64)
customProgramNew(myProg, myBuf)
addTextureToProgram(myProg, "images/myTexture.png")
addProgramToDisplay(myProg, "display")
customElementNew(mySprite, 0, 0, 100, 100, "white")
screenCenterElement(mySprite, "display", "XY")
addElementToBuffer(mySprite, myBuf)
updateElementToBuffer(mySprite, myBuf)
updateBuffer(myBuf)
end
function beatHit(beat)
setElementAngle(mySprite, beat * 15)
updateElementToBuffer(mySprite, myBuf)
updateBuffer(myBuf)
end
function dispose()
removeProgramFromDisplay(myProg, "display")
end- Scripts are compiled and executed via LuaJIT using the
linc_luajitbindings. - All callbacks silently no-op if the Lua function is not defined in the script.
- Multiple
.luafiles in the chart directory are all loaded and called in sequence. - The
##FUNKINVIEWLUA_FUNCTION_STOPreturn value halts execution for that callback across subsequent VMs. - Stage scripts are loaded from
stages/<stageName>.luarelative to the chart root.
But on one condition... You will most likely misuse the optimization techniques in your own game or overcomplicate them.
Please don't read this it's a joke I cocked up early in the wiki
Hello sidebar test.