High Level Peripherals
This is a fork of the mod Tom's Peripherals with minor additions and fixes
- Loaders
- NeoForge
- Downloads on Modrinth
- 304
- Followers on Modrinth
- 1
- versions
- 3
- Updated
- July 2, 2026Jul 2
- Published
- in 2026
At a glance
Needed both in the game and on the server
High Level Peripherals
A CC:Tweaked monitor peripheral that actually keeps up with what you want to build.
A fork of Tom's Peripherals, rebuilt to be far more usable, stable, and complete — bigger screens, real GPU-rendered shaders, full mouse/touch input, and a cleaner API that doesn't fight you.
✨ Features
🖥️ Big, sharp monitors
Build arrays up to 32x32 blocks (admin-adjustable up to 64x64), with per-block resolution up to 4096x4096. Draw with a full 2D API — filled shapes, gradients, text with custom fonts, image loading, double buffering — all running server-side, synced to every client automatically.
⚡ Real GPU shaders, not Lua math
Write a small GLSL-like shader once and it renders live on every viewer's own graphics card, at their own framerate, completely decoupled from the server tick. No more choking the game with per-pixel Lua raymarching — the shader engine handles that with zero Lua involved per pixel. Includes first-class raytracing primitives (sphere/plane/box intersection) so you're not hand-rolling ray math from scratch.
gpu.compileShader([[
color = vec3(uv.x, uv.y, sin(time));
]])That's it — it's already rendering.
🖱️ Full mouse input
Click, right-click, drag, and scroll directly on a monitor's screen face, with pixel-accurate coordinates delivered straight to your script. Respects whatever controls the player has configured — no hardcoded key assumptions. Perfect for building actual interactive UIs, not just static displays.
🎮 3D immediate-mode rendering
An OpenGL-1-style API (glBegin/glVertex/glTranslate/glRotate/lighting) for building real 3D scenes on a monitor without writing a shader — great for models, diagrams, or simple games.
🔆 Brightness control
Scale a monitor's output brightness in nits, from dim ambient displays to eye-searingly bright — applies to both drawn pixels and live shaders.
🧠 Room to build big
Generous default VRAM budget (~2 GiB per screen) shared across framebuffers, images, and shader textures, with server-configurable limits for bigger builds.
Why this fork?
Tom's Peripherals gave monitors and a GPU peripheral, but hit real ceilings fast — low resolution limits, no shader rendering, no mouse input, and a handful of long-standing multiblock/sync bugs. High Level Peripherals keeps the same familiar peripheral.find("tm_monitor") API you already know, and pushes every one of those ceilings out — so your builds stop being limited by the peripheral and start being limited by your imagination.
Getting started
local gpu = peripheral.find("tm_monitor")
gpu.fill(0x000000)
gpu.drawText(10, 10, "Hello, HLPS!", 0xFFFFFF)Full API reference below.
Docs
tm_monitor Lua API Reference
Complete reference for the monitor peripheral: 2D drawing, text, images, the 3D immediate-mode API, mouse/scroll input, and the shader engine.
1. Limits
| Limit | Value |
|---|---|
| Grid size | Up to 32x32 blocks by default (maxScreenSize, 1-64). Over limit → every call throws. |
gpu.setSize(n) | 64–4096 px/axis, default 1024. |
gpu.setGpuResolution(w, h) | 64–4096 px/axis, or (0,0) for auto. |
| VRAM budget | ~2 GiB default (maxVRAMSize), shared across framebuffer/images/buffers/textures. |
2. Color format
Packed 0xRRGGBB ints, or (r,g,b[,a]) 0-255 each.
- Alpha
0or255→ opaque. - Alpha
1..254→ source-over blend:out = (src*a + dst*(255-a)) / 255. getPixel/getpxreturn0xRRGGBB(alpha stripped).
3. Sizing
| Call | Effect |
|---|---|
gpu.setSize(n) | Per-block resolution, one axis. Total canvas is (blocksWide*n) x (blocksTall*n). |
gpu.getSize() | → width, height, cols, rows, blockSize. |
gpu.getResolution() | → width, height. |
gpu.refreshSize() | Forces a re-scan of the array's grid layout. |
gpu.debugTiling() | → string dump of live server-side tiling state, for diagnosing a wrong-looking array. |
Keep setSize low (64-256) if drawing per-pixel in Lua — use the shader engine (section 8) instead for anything procedural.
4. Basic 2D drawing
All coordinates are 1-based.
| Call | Notes |
|---|---|
gpu.fill([color]) | Fills the canvas. Default black. |
gpu.clear([color]) | Alias of fill. |
gpu.setPixel(x, y, color) | Out-of-bounds writes ignored. |
gpu.getPixel(x, y) | → int, 0 if out of bounds. |
gpu.filledRectangle(x, y, w, h, [color]) | Throws if out of bounds. |
gpu.rectangle(x, y, w, h, [color]) | Outline only. |
gpu.line(x1, y1, x2, y2, [color]) | Bresenham. |
gpu.lineS(x1, y1, x2, y2, [color]) | Alternate algorithm. |
gpu.circle(cx, cy, r, [color]) | Outline. |
gpu.fillCircle(cx, cy, r, [color]) | Filled. |
gpu.triangle(x1,y1, x2,y2, x3,y3, [color]) | Outline. |
gpu.fillTriangle(x1,y1, x2,y2, x3,y3, [color]) | Filled. |
gpu.gradientRectangle(x, y, w, h, c1, c2, [vertical]) | Linear interpolation between two colors. |
gpu.copy(srcX, srcY, w, h, dstX, dstY) | Overlap-safe. Capped at 64M px. |
gpu.scroll(dx, dy, [fillColor]) | Shifts canvas; exposed area filled. Same cap. |
gpu.drawBuffer(x, y, w, scale, ...pixels) | Row-major pixels as vararg numbers, not a table. |
5. Double buffering
gpu.setDoubleBuffered(true)
-- ... draw calls ...
gpu.flush() -- presents + syncsgpu.sync() pushes the framebuffer directly (throttled internally) — not usually needed outside double-buffering/shader flows.
6. Text
| Call | Notes |
|---|---|
gpu.drawText(x, y, text, [fg=-1], [bg=-1], [size=1], [pad=1]) | -1 skips that layer. |
gpu.drawTextSmart(x, y, text, [fg], [bg], [forceUnicode=false], [size=1], [pad=1]) | Auto ASCII/unicode fonts (ASCII draws at 2x size). |
gpu.getTextLength(text, [size=1], [pad=1]) | → pixel width. |
gpu.getFont() | → name, editable. |
gpu.setFont(name) | No-op if font doesn't exist. |
gpu.drawChar(x, y, charIndex, [fg], [bg], [size=1]) | Draws one glyph, 1-based index. |
gpu.addNewChar(char, width, ...16 numbers) | → int. Font must be editable. |
gpu.delChar(char) / gpu.freeChars() / gpu.clearChars() | Glyph management. |
gpu.setFontDefaultCharID(id) / gpu.getFontDefaultCharID() | Fallback glyph. |
7. Images and buffers
| Call | Returns |
|---|---|
gpu.newImage(w, h) | Blank LuaImage. |
gpu.imageFromBuffer(w, ...pixels) | LuaImage from raw pixel data. |
gpu.decodeImage(bufferRef) / gpu.decodeImage(...bytes) | Decodes PNG/etc. |
gpu.newBuffer([initialCapacity=32]) | Growable LuaByteBuffer. |
gpu.drawImage(x, y, image) | Draws, clipped to canvas. |
All charge VRAM (w*h*4 bytes) up front and throw if over budget.
8. The shader engine
Write a small GLSL-like program once; it runs per-pixel on the real GPU, no Lua involved.
gpu.compileShader([[
float sdSphere(vec3 p, float r) { return length(p) - r; }
uniform float speed;
color = vec3(uv.x, uv.y, sin(time * speed));
]])
gpu.setUniform("speed", 2.0)There's no main — code after the function defs runs top-to-bottom; color is the pixel output. gpu.clearShader() returns to normal 2D drawing.
gpu.isGpuMode()→truewhile a shader is live.gpu.setUniform(name, value)/gpu.setUniform3(name, x, y, z)— live-update, no recompile.gpu.setGpuResolution(w, h)— resolution the shader renders at (independent ofsetSize). Match your array'scols:rowsshape or don't call it.
A live shader has no ongoing tie to the computer that started it — it stops only if the last attached computer disconnects, or you call gpu.clearShader().
texture() isn't GPU-accelerated yet — a texture-sampling shader compiles fine but won't auto-render. Drive it yourself:
gpu.compileShader(src)
gpu.uploadTexture(0, w, h, pixels)
while true do
gpu.runShader(os.clock())
gpu.sync()
sleep(0)
endShader language
- Two types:
float,vec3. - Predefined:
color(write this),uv(.x/.y0..1,.y=0at top),res,time. uniform <float|vec3> name;- Functions:
<float|vec3> name(params) { ...; return expr; }. No recursion. Must appear before any other top-level statement, includinguniformdeclarations. - Control flow:
if/else,for (init; cond; update) {},break;,return expr;. - Operators:
+ - * /, unary- !, comparisons,&& ||,.x .y .zswizzle.vec3 * floatworks;float * vec3does not. - Built-ins:
vec3(f,f,f),sin cos tan sqrt abs floor fract exp pow mod min max step clamp mix smoothstep length dot cross normalize reflect,texture(slot, u, v). - Raytracing builtins (return nearest positive hit distance, or
-1.0for a miss):intersectSphere(ro, rd, center, radius),intersectPlane(ro, rd, planeY),intersectBox(ro, rd, center, halfSize).
9. Memory
| Call | Returns |
|---|---|
gpu.getUsedMemory() | Bytes currently used. |
gpu.getMaxMemory() | VRAM budget for this peripheral. |
10. Mouse & scroll input
Fires only while looking at the screen face with no GUI open. Sneaking has no effect. Keyboard input isn't captured here — that's a separate keyboard peripheral.
| Event | Args | Trigger |
|---|---|---|
tm_monitor_touch | side, x, y, soft | Whichever button is bound to "use" (right-click by default). soft is always false. |
tm_monitor_mouse | side, x, y, button, action | Mouse button press/release. |
tm_monitor_scroll | side, x, y, dir | dir is 1 (up) or -1 (down). |
tm_monitor_drag | side, x, y, button | Cursor moved this tick while held. Once per client tick. |
x, y are screen-absolute (1-based) across the whole array.
while true do
local ev, side, x, y, button, action = os.pullEvent("tm_monitor_mouse")
gpu.setPixel(x, y, 0xFF0000)
gpu.sync()
end11. 3D immediate-mode API
gpu3d = gpu.createWindow3D(...)
gpu3d.glFrustum(70, 0.1, 100)
gpu3d.glLoadIdentity()
gpu3d.glTranslate(0, 0, -5)
gpu3d.glDirLight(0, -1, 0)
gpu3d.glBegin(4) -- triangles
gpu3d.glColor(0xFF0000)
gpu3d.glVertex(-1, -1, 0)
gpu3d.glVertex( 1, -1, 0)
gpu3d.glVertex( 0, 1, 0)
gpu3d.glEnd()
gpu3d.render()
gpu3d.sync()| Call | Notes |
|---|---|
glFrustum(fov, near, far) | Perspective projection. |
glDirLight(x, y, z) | Directional light. |
glLoadIdentity() / glPushMatrix() / glPopMatrix() | Model matrix stack. |
glTranslate / glScale / glRotate(angleDeg, x,y,z) | |
glBegin([mode=4]) / glEnd() | Start/finish a primitive. |
glVertex(x,y,z) | Appends a vertex with current color/UV. |
glTexCoord(u,v) | UV for the next vertex. |
glColor(color) | Packed int or (r,g,b[,a]). |
glEnable(3553) / glDisable(3553) | Only GL_TEXTURE_2D supported. |
glGenTextures() / glDeleteTextures / glBindTexture / glTexImage | Texture management. |
getConstants() | GL constant name→value table. |
render() | Culls, lights, clips, projects, sorts, blits since last clear(). |
clear() | Wipes canvas + triangles + resets matrix/depth buffer. |
12. Performance
All drawing is server-side Java, always — singleplayer, LAN, or dedicated. Pushing pixels to the client is the one real client cost, done via a bulk memory copy.
Per-pixel Lua drawing (raymarching, plasma, etc.) will choke a client — use the shader engine instead, which runs on the GPU with zero Lua per pixel.
13. Brightness (nits)
gpu.setBrightness(200) -- 2x normal
print(gpu.getBrightness())nits / 100 scales final pixel output — 100 is default (1.0x). Applies to both CPU drawing and GPU shaders. Values that would exceed 255 clip instead of wrapping. Range is server-configurable (minBrightnessNits/maxBrightnessNits, default 10–400).
FAQ
How to install High Level Peripherals?
Install NeoForge for your Minecraft version, download High Level Peripherals for the same version and loader, put the file into the mods folder and launch the game with the loader profile.
Which Minecraft versions does High Level Peripherals support?
High Level Peripherals has builds for Minecraft 1.21.1.
Is High Level Peripherals needed on the server?
Needed in the game and on the server