DirectGPU , VS Compatible, Sable compatible, Create support, 1.0.20 pre-release: NeoForge 1.21.1. Forge 1.20.1:inprogress
June 1, 2026 · View on GitHub
High-Performance Graphics for ComputerCraft
Turn ComputerCraft monitors into high-resolution RGB displays with fast 2D drawing, 3D rendering, JPEG loading, input events, controller support, and extra world data.
What this mod is
DirectGPU is a ComputerCraft peripheral that lets Lua draw directly to monitor-backed displays in full RGB instead of the normal terminal palette.
You can use it for:
- high-resolution 2D UI
- images and JPEG viewers
- 3D scenes and OBJ models
- controller-driven programs and games
- touch input
- custom dashboards and world displays
It works with normal monitors, supports Valkyrien Skies compatibility, and is meant for cases where vanilla monitor text is too limited.
Quick answers to common questions
What is a displayId?
A displayId is the number returned when you create a display.
You use that number in later calls so DirectGPU knows which display to draw to.
local displayId = gpu.autoDetectAndCreateDisplay()
gpu.clear(displayId, 0, 0, 0)
gpu.updateDisplay(displayId)
What is a textureId?
A textureId is the number returned by loadTexture() or loadTextureFromImage().
It is just a handle for a loaded texture.
You do not pass the raw image every frame after that. You load it once, get a textureId, and then reference that texture later.
local textureId = gpu.loadTexture(width, height, base64PixelData)
print(textureId)
What is base64PixelData?
It is your raw image pixel bytes encoded as a Base64 string.
For textures, that usually means the pixel bytes for the image packed into a string, then Base64 encoded so it can be passed through Lua more easily.
Think of it like this:
- you have raw pixel bytes
- you encode them as Base64
- you send that string to DirectGPU
- DirectGPU decodes it and stores it as a texture
What is imageData?
imageData is a table containing:
widthheightpixels
The code expects all 3 to exist.
local imageData = {
width = 64,
height = 64,
pixels = someByteArray
}
loadTextureFromImage(imageData) uses that table to create a texture.
What is pointsObj?
pointsObj is a Lua table of points used by polygon, polyline, and bezier functions.
Use it like this:
local points = {
{10, 10},
{50, 20},
{80, 60},
{20, 90}
}
gpu.drawPolylines(displayId, points, 255, 255, 255)
For most cases, just treat it as:
{{x1, y1}, {x2, y2}, {x3, y3}}
What fonts are supported?
Text rendering uses your system fonts. So if your PC has a font installed, DirectGPU can usually use it.
You can also ask DirectGPU for the available font list:
local fonts = gpu.getAvailableFonts()
for i = 1, math.min(#fonts, 20) do
print(fonts[i])
end
In multiplayer, do other players need the same font installed?
Normally no for the final display output itself, because what ends up on the display is rendered pixels. The display shows the result, not the other player's local font list.
Why do I need updateDisplay()?
Most drawing functions modify the display buffer.
updateDisplay(displayId) pushes that finished frame to the actual display.
A common pattern is:
gpu.clear(displayId, 0, 0, 0)
-- draw stuff
gpu.updateDisplay(displayId)
Features
2D graphics
- 24-bit RGB color
- high resolution displays
- lines, rectangles, circles, ellipses, polygons, stars, rounded rects, bezier curves, SVG-like paths
- text rendering with system fonts
- pixel reads and writes
3D graphics
- camera setup and positioning
- z-buffer support
- cubes, spheres, pyramids
- OBJ model loading
- textured models
- ambient and directional lighting
- optional phong shading and backface culling
Images
- JPEG decoding
- scaled JPEG decoding
- fullscreen and region JPEG loading
- texture loading from raw image data
- cache and network helper functions
Input and interaction
- display mouse/touch events
- controller input and mappings
- server-side controller queries
- calibration helpers
Other extras
- world info queries
- map reader peripheral
- metaballs
- vector graphics helpers
Installation
- Download the latest DirectGPU jar from releases
- Put it in your Minecraft
modsfolder - Make sure you also have:
- Minecraft
1.20.1 - Forge
47.3.0+ - CC: Tweaked
1.20.1(1.116.1recommended)
- Minecraft
- Launch the game and place/craft the DirectGPU block
Recipe
[Iron] [Gold] [Iron]
[Redstone] [Computer] [Redstone]
[Iron] [Redstone] [Iron]
``$
\text{Required}:
- 4 \times \text{Iron} \text{Ingots}
- 1 \times \text{Gold} \text{Ingot}
- 3 \times \text{Redstone} \text{Dust}
- 1 \times \text{normal} \text{ComputerCraft} \text{computer}
---
## \text{Quick} \text{start}
### \text{Minimal} 2\text{D} \text{example}
$``lua
local gpu = peripheral.find("directgpu")
local display = gpu.autoDetectAndCreateDisplay()
gpu.clear(display, 20, 20, 30)
gpu.fillRect(display, 10, 10, 80, 40, 255, 0, 0)
gpu.drawText(display, "Hello DirectGPU", 12, 65, 255, 255, 255, "Arial", 18, "bold")
gpu.updateDisplay(display)
Minimal 3D example
local gpu = peripheral.find("directgpu")
local display = gpu.autoDetectAndCreateDisplay()
gpu.setupCamera(display, 60, 0.1, 1000)
gpu.setCameraPosition(display, 0, 2, 5)
gpu.addAmbientLight(display, 60, 60, 60, 0.3)
gpu.addDirectionalLight(display, 0, -1, 0, 255, 255, 255, 0.8)
local rot = 0
while true do
gpu.clear(display, 0, 0, 0)
gpu.clearZBuffer(display)
gpu.drawCube(display, 0, 0, 0, 2, rot, rot, 0, 255, 120, 120)
gpu.updateDisplay(display)
rot = rot + 2
sleep(0.05)
end
1. Create a display
You either auto-detect a monitor or manually create a display.
2. Draw into its buffer
Functions like clear, fillRect, drawText, drawCube, and loadJPEGFullscreen write to that display's internal framebuffer.
3. Push the frame
Call updateDisplay(displayId) when you want the current frame shown.
4. Reuse loaded resources
Things like textures and models return ids. Keep those ids and reuse them instead of reloading constantly.
Data formats and terminology
Colors
Most color parameters are standard RGB integer values:
r, g, b
Each channel is usually 0 to 255.
Examples:
- red =
255, 0, 0 - green =
0, 255, 0 - blue =
0, 0, 255 - white =
255, 255, 255 - black =
0, 0, 0
Coordinates
For 2D drawing, positions are pixel coordinates on the display.
x, y
Top-left is the usual starting point.
Point lists
Functions using pointsObj expect a Lua table of point tables.
local pts = {
{0, 0},
{20, 30},
{40, 10}
}
Image pixel data
For textures and map/image style operations, image data is raw pixel bytes plus width/height metadata.
Model ids and texture ids
If a function loads something and returns a number, that number is usually an id you store and use later.
API reference
Display management
autoDetectAndCreateDisplay() → number
Finds a nearby monitor automatically, creates a DirectGPU display for it, and returns the new displayId.
local displayId = gpu.autoDetectAndCreateDisplay()
autoDetectAndCreateDisplayWithResolution(resolutionMultiplier) → number
Same as above, but lets you pick the resolution multiplier when creating the display.
local displayId = gpu.autoDetectAndCreateDisplayWithResolution(0)
autoDetectMonitor() → string
Returns the detected monitor peripheral name. Useful if you want to know what monitor would be used.
local monitorName = gpu.autoDetectMonitor()
clearAllDisplays()
Removes every DirectGPU display owned by this GPU block.
gpu.clearAllDisplays()
createDisplay(x, y, z, facing, width, height) → number
Creates a display manually at a specific position and facing.
Returns the new displayId.
local displayId = gpu.createDisplay(0, 64, 0, "north", 3, 2)
createDisplayAt(x, y, z, facing, width, height) → number
Manual display creation helper. Use it the same way as createDisplay if you want to specify exact placement.
local displayId = gpu.createDisplayAt(0, 64, 0, "north", 3, 2)
createDisplayWithResolution(x, y, z, facing, width, height, resolutionMultiplier) → number
Manual display creation with a custom resolution multiplier. Higher settings mean more pixels.
local displayId = gpu.createDisplayWithResolution(0, 64, 0, "north", 3, 2, 1)
getDisplayInfo(displayId) → table
Returns info about a display. This includes values like:
idx,y,zfacingwidth,heightpixelWidth,pixelHeightresolutionMultiplier
local info = gpu.getDisplayInfo(displayId)
print(info.pixelWidth, info.pixelHeight)
getResourceStats() → string
Returns current resource stats for DirectGPU. Use it to inspect limits/usage.
local stats = gpu.getResourceStats()
print(stats)
listDisplays() → table
Returns a list of display ids currently owned by this block.
local displays = gpu.listDisplays()
removeDisplay(displayId) → boolean
Deletes one display.
Returns true if it was removed.
local ok = gpu.removeDisplay(displayId)
setDisplayPersistent(displayId, persistent)
Marks whether the display should persist.
gpu.setDisplayPersistent(displayId, true)
updateDisplay(displayId)
Pushes the current framebuffer to the display. Call this after drawing.
gpu.updateDisplay(displayId)
2D drawing
clear(displayId, r, g, b)
Fills the whole display with one color.
gpu.clear(displayId, 0, 0, 0)
drawCircle(displayId, cx, cy, radius, r, g, b, filled)
Draws a circle. Set filled to true for a solid circle.
gpu.drawCircle(displayId, 100, 80, 30, 255, 0, 0, true)
drawEllipse(displayId, cx, cy, rx, ry, r, g, b, filled)
Draws an ellipse. Set filled to true for a solid ellipse.
gpu.drawEllipse(displayId, 100, 80, 40, 20, 255, 255, 0, false)
drawLine(displayId, x1, y1, x2, y2, r, g, b)
Draws a line between two points.
gpu.drawLine(displayId, 0, 0, 100, 100, 255, 255, 255)
drawPolygon(displayId, pointsObj, r, g, b)
Draws a polygon from a point table.
pointsObj should be like {{x1, y1}, {x2, y2}, {x3, y3}}.
gpu.drawPolygon(displayId, {
{20, 20},
{80, 20},
{50, 70}
}, 0, 255, 0)
drawPolylines(displayId, pointsObj, r, g, b)
Draws connected line segments through the given points. Needs at least 2 points.
gpu.drawPolylines(displayId, {
{10, 10},
{20, 30},
{50, 40},
{90, 10}
}, 255, 255, 255)
fillEllipse(displayId, cx, cy, rx, ry, r, g, b)
Draws a filled ellipse.
gpu.fillEllipse(displayId, 100, 80, 40, 20, 0, 200, 255)
fillRect(displayId, x, y, w, h, r, g, b)
Draws a filled rectangle.
gpu.fillRect(displayId, 10, 10, 60, 30, 255, 0, 0)
getPixel(displayId, x, y) → table
Reads one pixel and returns its color.
local pixel = gpu.getPixel(displayId, 10, 10)
setPixel(displayId, x, y, r, g, b)
Sets one pixel.
gpu.setPixel(displayId, 10, 10, 255, 255, 255)
Text rendering
clearFontCache()
Clears cached font renderers. Useful if you changed fonts/styles a lot and want to reset font cache state.
gpu.clearFontCache()
drawText(displayId, text, x, y, r, g, b, fontName, fontSize, style) → table
Draws text with the given system font, size, and style.
Useful styles depend on the font setup, but common ones are things like "plain", "bold", or "italic".
gpu.drawText(displayId, "Hello", 10, 20, 255, 255, 255, "Arial", 18, "bold")
drawTextWithBg(displayId, text, x, y, fgR, fgG, fgB, bgR, bgG, bgB, padding, fontName, fontSize, style) → table
Draws text with a background box behind it.
gpu.drawTextWithBg(displayId, "Button", 20, 20,
255, 255, 255,
40, 40, 40,
4,
"Arial", 16, "bold")
drawTextWrapped(displayId, text, x, y, maxWidth, r, g, b, lineSpacing, fontName, fontSize, style) → table
Draws wrapped text constrained to maxWidth.
Returns a table containing:
widthheightlines
local info = gpu.drawTextWrapped(displayId, longText, 10, 10, 200,
255, 255, 255, 2, "Arial", 16, "plain")
print(info.lines)
measureText(text, fontName, fontSize, style) → table
Measures text size without drawing it. Useful for layout.
local size = gpu.measureText("Hello", "Arial", 18, "bold")
getAvailableFonts() → table
Returns available system font family names.
local fonts = gpu.getAvailableFonts()
Image and JPEG
clearJPEGCache()
Clears cached JPEG decode data.
gpu.clearJPEGCache()
decodeAndScaleJPEG(base64JpegData, targetWidth, targetHeight) → table
Decodes a JPEG from Base64 and scales it to the given size. Useful when you want image data first instead of drawing directly.
local image = gpu.decodeAndScaleJPEG(base64JpegData, 128, 128)
decodeJPEG(base64JpegData) → table
Decodes a Base64 JPEG and returns image data.
local image = gpu.decodeJPEG(base64JpegData)
getJPEGDimensions(base64JpegData) → table
Returns the width and height of a Base64 JPEG without fully drawing it.
local dims = gpu.getJPEGDimensions(base64JpegData)
getJPEGNetworkStats() → string
Returns JPEG/network related stats.
print(gpu.getJPEGNetworkStats())
getRecommendedJPEGSettings(targetWidth, targetHeight) → table
Returns suggested JPEG settings for a target size. Useful when you are trying to optimize transfer size or decode cost.
local settings = gpu.getRecommendedJPEGSettings(320, 180)
loadJPEGFullscreen(displayId, base64JpegData)
Decodes a Base64 JPEG and fills the entire display with it.
gpu.loadJPEGFullscreen(displayId, base64JpegData)
gpu.updateDisplay(displayId)
loadJPEGRegion(displayId, jpegBinaryData, x, y, w, h)
Loads JPEG binary data into only part of the display. Use this when updating a sub-region.
gpu.loadJPEGRegion(displayId, jpegBinaryData, 0, 0, 100, 100)
loadJPEGRegionBytes(displayId, base64JpegData, x, y, w, h)
Same idea as loadJPEGRegion, but accepts Base64 JPEG data.
gpu.loadJPEGRegionBytes(displayId, base64JpegData, 50, 50, 128, 128)
preloadJPEGSequence(displayId, jpegSequence)
Preloads a sequence of JPEG frames or images for later use. Useful for slideshows, animations, or video-like playback.
gpu.preloadJPEGSequence(displayId, jpegSequence)
Dictionary compression
These functions help reduce repeated Base64/image transfer overhead by storing and reusing chunks.
clearDictionary()
Clears the compression dictionary.
gpu.clearDictionary()
compressWithDict(base64Data) → table
Compresses Base64 data using the current dictionary.
local packed = gpu.compressWithDict(base64Data)
decompressFromDict(hashMap) → string
Decompresses dictionary-compressed data back into the original form.
local data = gpu.decompressFromDict(hashMap)
getChunk(hash) → string
Returns a stored dictionary chunk by hash.
local chunk = gpu.getChunk(hash)
getDictionaryStats() → string
Returns dictionary usage stats.
print(gpu.getDictionaryStats())
hasChunk(hash) → boolean
Checks whether a given chunk hash exists.
if gpu.hasChunk(hash) then
print("chunk exists")
end
3D camera
clearZBuffer(displayId)
Clears the depth buffer for the display. Call this before drawing a new 3D frame.
gpu.clearZBuffer(displayId)
getCameraInfo(displayId) → table
Returns current camera settings for the display.
local info = gpu.getCameraInfo(displayId)
lookAt(displayId, targetX, targetY, targetZ)
Points the camera toward a world/render target point.
gpu.lookAt(displayId, 0, 0, 0)
setCameraPosition(displayId, x, y, z)
Moves the camera.
gpu.setCameraPosition(displayId, 0, 2, 5)
setCameraRotation(displayId, pitch, yaw, roll)
Sets camera rotation directly.
gpu.setCameraRotation(displayId, 10, 30, 0)
setCameraTarget(displayId, x, y, z)
Sets a camera target point.
gpu.setCameraTarget(displayId, 0, 0, 0)
setupCamera(displayId, fov, near, far) → table
Initializes the camera projection.
Typical values are something like 60, 0.1, 1000.
gpu.setupCamera(displayId, 60, 0.1, 1000)
3D primitives
clear3D(displayId)
Clears 3D-related state for the display.
gpu.clear3D(displayId)
drawCube(displayId, x, y, z, size, rotX, rotY, rotZ, r, g, b)
Draws a cube.
gpu.drawCube(displayId, 0, 0, 0, 2, 0, 45, 0, 255, 100, 100)
drawPyramid(displayId, x, y, z, size, rotX, rotY, rotZ, r, g, b)
Draws a pyramid.
gpu.drawPyramid(displayId, 0, 0, 0, 2, 0, 45, 0, 255, 255, 0)
drawSphere(displayId, x, y, z, radius, segments, r, g, b, textureNameObj)
Draws a sphere.
segments controls detail level.
The last argument is texture-related if supported by your use case.
gpu.drawSphere(displayId, 0, 0, 0, 1, 16, 100, 180, 255, nil)
3D models
clearAll3DModels()
Clears all loaded 3D models.
gpu.clearAll3DModels()
draw3DModel(displayId, modelId, x, y, z, rotX, rotY, rotZ, scale, r, g, b)
Draws a loaded model using a flat color.
gpu.draw3DModel(displayId, modelId, 0, 0, 0, 0, 45, 0, 1.0, 255, 255, 255)
draw3DModelTextured(displayId, modelId, x, y, z, rotX, rotY, rotZ, scale, textureId)
Draws a loaded model with a previously loaded texture.
gpu.draw3DModelTextured(displayId, modelId, 0, 0, 0, 0, 45, 0, 1.0, textureId)
get3DModelInfo(modelId) → table
Returns info about a loaded model.
local info = gpu.get3DModelInfo(modelId)
load3DModel(objData) → number
Loads OBJ model data from a normal string and returns a modelId.
local modelId = gpu.load3DModel(objData)
load3DModelFromBytes(base64ObjData) → number
Loads OBJ model data from Base64 and returns a modelId.
local modelId = gpu.load3DModelFromBytes(base64ObjData)
unload3DModel(modelId) → boolean
Removes a loaded model.
gpu.unload3DModel(modelId)
3D lighting
addAmbientLight(displayId, r, g, b, intensity)
Adds ambient light affecting the whole scene evenly.
gpu.addAmbientLight(displayId, 40, 40, 40, 0.3)
addDirectionalLight(displayId, dirX, dirY, dirZ, r, g, b, intensity)
Adds a directional light shining in the given direction.
gpu.addDirectionalLight(displayId, 0, -1, 0, 255, 255, 255, 0.8)
clearLights(displayId)
Removes all lights from the display's 3D scene.
gpu.clearLights(displayId)
setBackfaceCulling(displayId, enabled)
Turns backface culling on or off. Useful for performance and cleaner solid models.
gpu.setBackfaceCulling(displayId, true)
setPhongShading(displayId, enabled)
Turns phong shading on or off. Use it if you want smoother lighting on supported geometry.
gpu.setPhongShading(displayId, true)
Textures
This was one of the most confusing parts before, so here is the plain version.
A texture is an image that gets stored by DirectGPU and then referenced by id.
Basic texture flow
- load image pixels into DirectGPU
- get a
textureId - use that texture id when drawing textured things
- unload it when done if needed
getTextureInfo(textureId) → table
Returns info about a texture. Currently this includes:
idwidthheight
local info = gpu.getTextureInfo(textureId)
print(info.width, info.height)
loadTexture(width, height, base64PixelData) → number
Loads a texture from raw pixel bytes encoded as Base64.
Returns a new textureId.
Use this when you already have the pixel bytes and know the width and height.
local textureId = gpu.loadTexture(64, 64, base64PixelData)
loadTextureFromImage(imageData) → number
Loads a texture from an image table. The image table must contain:
widthheightpixels
Returns a new textureId.
local textureId = gpu.loadTextureFromImage({
width = 64,
height = 64,
pixels = pixelBytes
})
unloadTexture(textureId) → boolean
Deletes a loaded texture from DirectGPU. Call this if you no longer need it.
gpu.unloadTexture(textureId)
Texture notes
textureIdis just the texture handle returned by DirectGPUbase64PixelDatais not a file path, it is the actual pixel data encoded as Base64imageDatais a table of width/height/pixels- loading a texture does not draw it by itself
- you load textures first, then reference them in textured rendering functions
Input events
clearEvents(displayId)
Clears queued display input events.
gpu.clearEvents(displayId)
hasEvents(displayId) → boolean
Checks whether the display has pending input events.
if gpu.hasEvents(displayId) then
print("input waiting")
end
pollEvent(displayId) → table | nil
Returns the next queued input event for that display, or nil if there is none.
Typical use:
if gpu.hasEvents(displayId) then
local event = gpu.pollEvent(displayId)
print(event.type)
end
World data
getBiomeAt(x, y, z) → string
Returns the biome at the given coordinates.
print(gpu.getBiomeAt(0, 64, 0))
getDimension() → string
Returns the current dimension.
print(gpu.getDimension())
getMoonInfo() → table
Returns current moon-related info.
local moon = gpu.getMoonInfo()
getTimeInfo() → table
Returns current world time info.
local t = gpu.getTimeInfo()
getWeather() → table
Returns weather info.
local weather = gpu.getWeather()
getWorldInfo() → table
Returns a broader set of world information.
local info = gpu.getWorldInfo()
Controller input
clearControllerEvents(controllerId)
Clears queued controller events.
gpu.clearControllerEvents(0)
getAxes(controllerId) → table
Returns all raw axis values for the controller.
local axes = gpu.getAxes(0)
getAxis(controllerId, axisIndex) → number
Returns one raw axis value.
local x = gpu.getAxis(0, 0)
getButton(controllerId, buttonIndex) → boolean
Returns whether one raw button is pressed.
if gpu.getButton(0, 0) then print("pressed") end
getButtons(controllerId) → table
Returns all raw button states.
local buttons = gpu.getButtons(0)
getControllerCount() → number
Returns how many controllers are currently visible to DirectGPU.
print(gpu.getControllerCount())
getControllerDeadzone() → number
Returns the current controller deadzone.
print(gpu.getControllerDeadzone())
getControllerInfo(controllerId) → table
Returns info about a controller.
local info = gpu.getControllerInfo(0)
hasControllerEvents(controllerId) → boolean
Checks whether the controller has queued events.
if gpu.hasControllerEvents(0) then print("controller events") end
pollControllerEvent(controllerId) → table | nil
Returns the next queued controller event.
local event = gpu.pollControllerEvent(0)
scanForControllers()
Scans for connected controllers. Run this before trying to use them.
gpu.scanForControllers()
setControllerDeadzone(deadzone)
Sets the controller deadzone.
gpu.setControllerDeadzone(0.12)
updateControllerState(controllerId)
Refreshes controller state.
gpu.updateControllerState(0)
Controller mapping
exportRawControllerState(controllerId) → string
Exports raw controller state for debugging or mapping work.
print(gpu.exportRawControllerState(0))
getControllerMapping(controllerId) → table
Returns the current raw mapping for a controller.
local mapping = gpu.getControllerMapping(0)
getMappedAxis(controllerId, axisName) → number
Returns a mapped axis by name.
local lx = gpu.getMappedAxis(0, "LEFT_STICK_X")
getMappedButton(controllerId, buttonName) → boolean
Returns a mapped button by name.
if gpu.getMappedButton(0, "A") then print("A") end
resetControllerMapping(controllerId)
Resets a controller's custom mapping.
gpu.resetControllerMapping(0)
saveControllerMappings()
Saves mappings.
gpu.saveControllerMappings()
setAxisMapping(controllerId, axisName, rawAxis, inverted)
Maps a named axis to a raw axis id.
gpu.setAxisMapping(0, "LEFT_STICK_X", 0, false)
setButtonMapping(controllerId, buttonName, rawButton)
Maps a named button to a raw button id.
gpu.setButtonMapping(0, "A", 0)
Controller profiles
getControllerAxisNames(controllerId) → table
Returns named axis labels known for that controller/profile.
local names = gpu.getControllerAxisNames(0)
getControllerButtonNames(controllerId) → table
Returns named button labels.
local names = gpu.getControllerButtonNames(0)
getControllerInputs(controllerId) → table
Returns the available named inputs.
local inputs = gpu.getControllerInputs(0)
getControllerProfile(controllerId) → table
Returns the profile for the controller.
local profile = gpu.getControllerProfile(0)
getControllerType(controllerId) → string
Returns the detected controller type.
print(gpu.getControllerType(0))
getNamedAxesActive(controllerId, threshold) → table
Returns named axes currently active above a threshold.
local active = gpu.getNamedAxesActive(0, 0.2)
getNamedAxis(controllerId, axisName) → number
Returns a named axis value.
local lx = gpu.getNamedAxis(0, "LEFT_STICK_X")
getNamedButton(controllerId, buttonName) → boolean
Returns a named button state.
if gpu.getNamedButton(0, "A") then print("A down") end
getNamedButtonsPressed(controllerId) → table
Returns named buttons currently pressed.
local pressed = gpu.getNamedButtonsPressed(0)
hasInput(controllerId, inputName) → boolean
Checks if a named input exists on that controller/profile.
print(gpu.hasInput(0, "RIGHT_TRIGGER"))
refreshControllerProfile(controllerId)
Refreshes the controller profile detection.
gpu.refreshControllerProfile(0)
Server-side controllers
These let server-side Lua query controller state linked to a specific player.
getPlayerUUID() → string
Returns your current player UUID.
local uuid = gpu.getPlayerUUID()
getServerControllerAxes(playerUUID, localControllerId) → table
Gets all controller axes for a player's controller.
local axes = gpu.getServerControllerAxes(uuid, 0)
getServerControllerAxis(playerUUID, controllerId, axisIndex) → number
Gets one raw axis from a player's controller.
local axis = gpu.getServerControllerAxis(uuid, 0, 0)
getServerControllerButton(playerUUID, controllerId, buttonIndex) → boolean
Gets one raw button from a player's controller.
local pressed = gpu.getServerControllerButton(uuid, 0, 0)
getServerControllerButtons(playerUUID, localControllerId) → table
Gets all raw button states from a player's controller.
local buttons = gpu.getServerControllerButtons(uuid, 0)
getServerControllerCount(playerUUID) → number
Gets how many controllers that player currently has available.
print(gpu.getServerControllerCount(uuid))
getServerControllerInfo(playerUUID, localControllerId) → table
Gets info about one of that player's controllers.
local info = gpu.getServerControllerInfo(uuid, 0)
getServerControllerState(playerUUID, controllerId) → table
Gets broader state for a player's controller.
local state = gpu.getServerControllerState(uuid, 0)
hasServerController(playerUUID, localControllerId) → boolean
Checks if the player has that controller id available.
print(gpu.hasServerController(uuid, 0))
Vector graphics
drawBezierCurve(displayId, pointsObj, r, g, b, segmentsObj)
Draws a bezier curve from a point table.
If segmentsObj is omitted, it uses a default segment count.
gpu.drawBezierCurve(displayId, {
{10, 80},
{40, 10},
{90, 10},
{120, 80}
}, 255, 255, 255, 50)
drawRoundedRect(displayId, x, y, w, h, radius, r, g, b, filled)
Draws a rounded rectangle.
gpu.drawRoundedRect(displayId, 10, 10, 100, 40, 8, 0, 200, 255, true)
drawSVGPath(displayId, pathData, x, y, scale, r, g, b)
Draws a path from SVG-style path data. Useful for icons or vector shapes.
gpu.drawSVGPath(displayId, "M 0 0 L 10 0 L 10 10 Z", 50, 50, 4, 255, 255, 255)
drawStar(displayId, cx, cy, points, outerRadius, innerRadius, r, g, b, filled)
Draws a star.
gpu.drawStar(displayId, 80, 80, 5, 30, 15, 255, 255, 0, true)
Metaballs
addMetaball(systemId, x, y, radius, strength) → number
Adds one metaball to a metaball system.
local ballId = gpu.addMetaball(systemId, 50, 50, 20, 1.0)
clearMetaballs(systemId)
Removes all metaballs from a system.
gpu.clearMetaballs(systemId)
createMetaballSystem(displayId) → number
Creates a metaball system for a display and returns systemId.
local systemId = gpu.createMetaballSystem(displayId)
getMetaballCount(systemId) → number
Returns the number of metaballs in a system.
print(gpu.getMetaballCount(systemId))
getMetaballInfo(systemId, ballId) → table
Returns info for one metaball.
local info = gpu.getMetaballInfo(systemId, ballId)
removeMetaballSystem(systemId)
Deletes a metaball system.
gpu.removeMetaballSystem(systemId)
renderMetaballs(systemId, threshold, renderMode)
Renders the metaballs to the display.
gpu.renderMetaballs(systemId, 1.0, 0)
setMetaballColor(systemId, ballId, r, g, b)
Sets the color of one metaball.
gpu.setMetaballColor(systemId, ballId, 0, 255, 255)
setMetaballPhysics(systemId, enabled, gravity, drag)
Turns metaball physics on or off and sets gravity/drag.
gpu.setMetaballPhysics(systemId, true, 0.1, 0.01)
setMetaballVelocity(systemId, ballId, vx, vy)
Sets one metaball's velocity.
gpu.setMetaballVelocity(systemId, ballId, 2, -1)
updateMetaballs(systemId, deltaTime)
Advances metaball simulation.
gpu.updateMetaballs(systemId, 0.05)
Calibration
getCalibrationValues() → table
Returns current calibration settings/values.
local values = gpu.getCalibrationValues()
setCalibrationMode(enabled, divisor, subtract)
Sets calibration mode values.
gpu.setCalibrationMode(true, 2, 10)
Map reader peripheral
The map reader is a separate peripheral type: map_reader
It reads Minecraft map items from its internal 9-slot inventory. It can return map ids, metadata, decorations, and full pixel data.
Basic usage
local reader = peripheral.find("map_reader")
local maps = reader.scanAll()
scanAll() → table
Scans all map items in the internal inventory. Returns a list of map entries.
local maps = reader.scanAll()
scanInternal() → table
Alias of scanAll().
local maps = reader.scanInternal()
scanAdjacent() → table
Currently returns an empty list. Adjacent container scanning is not supported anymore.
local maps = reader.scanAdjacent()
getMapCounts() → table
Returns counts of found maps.
local counts = reader.getMapCounts()
getAdjacentContainers() → table
Currently returns an empty list.
local containers = reader.getAdjacentContainers()
readMap(mapId) → table
Reads one map by map id, not slot number. Returns map data such as:
scaledimensioncenterX,centerZlockedpixelsdecorations
pixels is Base64-encoded RGB pixel data for the map image.
local mapData = reader.readMap(mapId)
print(mapData.dimension)
print(#mapData.decorations)
Full example
local reader = peripheral.find("map_reader")
local maps = reader.scanAll()
print("Found " .. #maps .. " maps")
for _, mapInfo in ipairs(maps) do
print(string.format("Map #%d in slot %d: %s",
mapInfo.mapId, mapInfo.slot, mapInfo.displayName))
end
if #maps > 0 then
local mapId = maps[1].mapId
local mapData = reader.readMap(mapId)
print("Scale:", mapData.scale)
print("Dimension:", mapData.dimension)
print("Center:", mapData.centerX, mapData.centerZ)
print("Locked:", mapData.locked)
print("Decorations:", #mapData.decorations)
end
Examples
Interactive drawing board
local gpu = peripheral.find("directgpu")
local display = gpu.autoDetectAndCreateDisplay()
gpu.clear(display, 20, 20, 30)
local drawing = false
local points = {}
local running = true
parallel.waitForAny(
function()
while running do
if gpu.hasEvents(display) then
local event = gpu.pollEvent(display)
if event and event.type == "mouse_click" then
drawing = true
points = {{event.x, event.y}}
elseif event and event.type == "mouse_drag" and drawing then
table.insert(points, {event.x, event.y})
gpu.drawPolylines(display, points, 0, 255, 255)
gpu.updateDisplay(display)
elseif event and event.type == "mouse_up" then
drawing = false
end
end
sleep(0.05)
end
end,
function()
while running do
local _, key = os.pullEvent("key")
if key == keys.q then running = false end
end
end
)
3D model viewer
local gpu = peripheral.find("directgpu")
local display = gpu.autoDetectAndCreateDisplay()
local objData = [[
v -1 -1 -1
v 1 -1 -1
v 1 1 -1
v -1 1 -1
f 1 2 3 4
]]
local modelId = gpu.load3DModel(objData)
gpu.setupCamera(display, 60, 0.1, 1000)
gpu.setCameraPosition(display, 0, 0, 5)
gpu.addDirectionalLight(display, 0, -1, 0, 255, 255, 255, 0.8)
local rotation = 0
while true do
gpu.clear(display, 0, 0, 0)
gpu.clearZBuffer(display)
gpu.draw3DModel(display, modelId, 0, 0, 0, 20, rotation, 0, 1.0, 200, 200, 255)
gpu.updateDisplay(display)
rotation = rotation + 1
sleep(0.05)
end
Controller test
local gpu = peripheral.find("directgpu")
gpu.scanForControllers()
if gpu.getControllerCount() == 0 then
print("No controller found")
return
end
while true do
local lx = gpu.getNamedAxis(0, "LEFT_STICK_X")
local ly = gpu.getNamedAxis(0, "LEFT_STICK_Y")
local a = gpu.getNamedButton(0, "A")
print("LX:", lx, "LY:", ly, "A:", a)
sleep(0.05)
end
Technical specs
| Specification | Value |
|---|---|
| Minecraft version | 1.20.1 |
| Forge | 47.3.0+ |
| CC: Tweaked | 1.20.1 |
| Max displays | 50 per world |
| Total pixel limit | 10 megapixels |
| Max monitor size | 16×16 blocks |
| Max resolution | up to 656×324 pixels per block |
| Color depth | 24-bit RGB |
| Render distance | 64 blocks |
| Target frame rate | up to 60 FPS |
Tips
- load models and textures once, then reuse ids
- clear + draw +
updateDisplay()is the normal frame pattern - clear z-buffer each 3D frame
- use
measureText()for layout instead of guessing text width - use
hasEvents()beforepollEvent() - use
scanForControllers()before reading controllers
Support
- Issues: GitHub Issues
- Discord: Join Server
- Docs: this README and the example scripts
License
This project is licensed under All Rights Reserved (ARR).
You may:
- use this mod in personal gameplay
- use this mod on servers
- create videos or streams featuring it
You may not:
- redistribute or reupload the mod
- modify and redistribute the mod
- reuse code from this mod in other projects without permission
Credits
Author: Tom
Special thanks:
- Minecraft Forge team
- CC: Tweaked developers
- DirectGPU testers and community users
Made for the ComputerCraft community