For openFrameworks Users

TrussC for
oF Users

If you love oF, you'll love TrussC.
Same joy, modern tech.

TrussC deeply respects the "joy of creative coding" that openFrameworks pioneered.

But hardware and C++ have changed dramatically since oF was designed in the 2000s.
TrussC inherits oF's spirit while optimizing for modern environments—a new foundation for the next decade.

What Stays the Same

oF users will feel right at home

setup, update, draw

The fundamental rhythm of creative coding. This structure is unchanged.

ofApp tcApp

Familiar Method Names

We kept method names you know and love to minimize the learning curve.

ofDrawCircle(x, y, r) drawCircle(x, y, r)

VSCode Support

It's the standard now. Cursor user? Of course that works too.

CMake + clangd

Pain Points Solved

TrussC's answers to long-standing oF frustrations

The Problem

Can't Draw Thick Lines

ofSetLineWidth() isn't guaranteed above 1px due to OpenGL limitations.
On macOS / Metal, it's completely ignored. Drawing thick lines meant generating meshes manually.

TrussC's Solution

Beautiful Strokes with StrokeMesh

In oF, thick lines had ugly clipped corners and endpoints.
StrokeMesh gives you beautiful strokes with proper edges.

// Draw lines of any thickness with StrokeMesh
StrokeMesh stroke;
stroke.setStrokeWidth(5.0f);  // 5px thick line
stroke.moveTo(0, 0);
stroke.lineTo(100, 100);
stroke.draw();

// Can also create from Polyline
Polyline line;
line.addVertex(0, 0);
line.addVertex(100, 50);
StrokeMesh stroke2(line, 3.0f);
stroke2.draw();
The Problem

Manual Parent-Child Management

ofNode exists but parent-child relationships require manual management.
Memory management gets complex when adding/removing children.
Children can outlive their parents, causing issues.

TrussC's Solution

Automatic Management with shared_ptr

// Automatic management with shared_ptr
auto parent = Node::create();
auto child = Node::create();
parent->addChild(child);

// When parent is destroyed, children are automatically released
// Circular references safely avoided with weak_ptr
The Problem

Hit Testing Rotated Objects is Painful

Mouse coordinates are always in screen space (top-left origin).
Hit testing on rotated/scaled objects requires complex calculations.
Even with ofNode, coordinate transforms had to be done manually.

TrussC's Solution

Automatic Local Coordinate Transform

// RectNode automatically transforms to local coordinates
class MyButton : public RectNode {
    void onMousePressed(const Vec2& localPos, int button) override {
        // localPos is in THIS node's coordinate space
        // Even if parent is rotated, you get local coords!
        if (localPos.x < width/2) {
            // Left half clicked
        }
    }
};
The Problem

Overlapping Object Hit Testing is Tedious

Hit testing overlapping objects is cumbersome.
Z-order aware hit testing had to be implemented from scratch.

TrussC's Solution

Z-Order Aware Event Consumption

// RectNode automatically respects Z-order
// Front nodes receive events first

void onMousePressed(const Vec2& pos, int button) override {
    // Handle here, and nodes behind won't receive it
    consumeEvent();
}
The Problem

No Safe Way to "Execute After 3 Seconds"

ofThread is prone to data races.
No standard way to safely "execute after N seconds".
Time-checking in update() leads to messy code.

TrussC's Solution

Safe Main-Thread Sync Timers

// Safe delayed execution on main thread
node->callAfter(3.0f, []() {
    // Runs on main thread after 3 seconds
    // No mutex needed, no data race worries
});

// Repeating execution is easy too
node->callEvery(1.0f, []() {
    // Runs every second
});
The Problem

OpenGL Deprecated on macOS

Apple deprecated OpenGL in favor of Metal.
Future compatibility and performance optimization became uncertain.
The looming question: when will support be dropped?

TrussC's Solution

Native Backends via Sokol

Metal / DirectX 12 / Vulkan / WebGPU support.
Runs on OS-recommended native APIs for optimal speed and power efficiency.
WebAssembly + WebGPU provides excellent browser portability.

The Problem

License Concerns for Commercial Use

oF itself is MIT, but dependencies include GPL libraries.
Checking licenses for FFmpeg, FreeType, etc. is tedious.
Uncertainty when using for client work.

TrussC's Solution

MIT / zlib / Public Domain Only

Zero GPL contamination guaranteed.
All dependencies are commercial-use friendly.
Use with confidence for client projects.

The Problem

Update and Draw Always Coupled

ofSetFrameRate() couples Draw and Update together.
Problematic when you need fixed timestep for physics simulation.
Hard to build event-driven apps (redraw only on button press).

TrussC's Solution

Independent Update & Draw Control

By default, update and draw alternate as usual. But for event-driven UI apps, you can avoid unnecessary draws by triggering them separately (e.g., on click).
For render-heavy apps, you can also run update at a fixed rate.

// Independent control of Draw and Update
setDrawVsync(true);    // Draw at VSync (60Hz)
setUpdateFps(120);      // Physics update at 120Hz

// Event-driven mode (power-saving)
setDrawFps(0);          // Disable auto-draw
// Call redraw() in mousePressed etc. to trigger draw

API Reference

oF functions → TrussC equivalents

App Structure

openFrameworks TrussC Notes
ofApp : public ofBaseApptcApp : public App
ofRunApp(new ofApp())runApp<tcApp>()Template style
setup()setup()Same
update()update()Same
draw()draw()Same
keyPressed(int key)keyPressed(int key)Same
mousePressed(x, y, button)mousePressed(x, y, button)Same
windowResized(w, h)windowResized(w, h)Same
dragEvent(ofDragInfo&)filesDropped(paths, x, y)
ofSetFrameRate(60)setFps(60)
ofSetVerticalSync(true)setVsync(true)
-setDrawFps(fps)Independent draw rate
-setUpdateFps(fps)Independent update rate
ofGetElapsedTimef()getElapsedTime()
ofGetFrameRate()getFrameRate()
ofGetFrameNum()getFrameCount()
ofGetWidth()getWindowWidth()
ofGetHeight()getWindowHeight()
ofExit()exitApp()

Graphics

openFrameworks TrussC Notes
ofBackground(r, g, b)clear(r, g, b)0.0-1.0
ofSetColor(r, g, b, a)setColor(r, g, b, a)0.0-1.0
ofSetColor(ofColor::red)setColor(colors::red)
ofDrawRectangle(x, y, w, h)drawRect(x, y, w, h)
ofDrawCircle(x, y, r)drawCircle(x, y, r)
ofDrawEllipse(x, y, w, h)drawEllipse(x, y, w, h)
ofDrawLine(x1, y1, x2, y2)drawLine(x1, y1, x2, y2)
ofDrawTriangle(...)drawTriangle(...)
ofNoFill()noFill()
ofFill()fill()
ofSetLineWidth(w)setLineWidth(w)
ofDrawBitmapString(s, x, y)drawBitmapString(s, x, y)
ofPathPath
-StrokeMeshThick lines with proper edges
ofEnableBlendMode(...)setBlendMode(...)
glScissor(...)setScissor(...)

Transform

openFrameworks TrussC Notes
ofPushMatrix()pushMatrix()
ofPopMatrix()popMatrix()
ofTranslate(x, y, z)translate(x, y, z)
ofRotateDeg(deg)rotateDeg(deg)
ofRotateRad(rad)rotateRad(rad)
ofScale(x, y, z)scale(x, y, z)

Math

openFrameworks TrussC Notes
glm::vec2 / ofVec2fVec2
glm::vec3 / ofVec3fVec3
glm::vec4 / ofVec4fVec4
glm::mat4 / ofMatrix4x4Mat4
ofMap(v, a, b, c, d)map(v, a, b, c, d)
ofClamp(v, min, max)clamp(v, min, max)
ofLerp(a, b, t)lerp(a, b, t)
ofNoise(x)noise(x)Perlin noise
ofSignedNoise(x)signedNoise(x)
ofRandom(min, max)random(min, max)
ofDegToRad(deg)radians(deg)
ofRadToDeg(rad)degrees(rad)
PIPI
TWO_PITAUτ = 2π

Color

openFrameworks TrussC Notes
ofColor(r, g, b, a)Color(r, g, b, a)0.0-1.0
ofColor::fromHsb(h, s, b)Color::fromHSB(h, s, b)0.0-1.0
-Color::fromOKLab(L, a, b)OKLab color space
-Color::fromOKLCH(L, C, h)OKLCH color space

Font

openFrameworks TrussC Notes
ofTrueTypeFontFont
font.load("font.ttf", size)font.load("font.ttf", size)Same
font.drawString(text, x, y)font.drawString(text, x, y)Same
font.stringWidth(text)font.getStringWidth(text)
font.stringHeight(text)font.getStringHeight(text)
font.getLineHeight()font.getLineHeight()Same

Image

openFrameworks TrussC Notes
ofImageImage
img.load("path")img.load("path")Same
img.draw(x, y)img.draw(x, y)Same
img.draw(x, y, w, h)img.draw(x, y, w, h)Same
img.save("path")img.save("path")Same
img.getWidth()img.getWidth()Same
img.getHeight()img.getHeight()Same
-img.setFilter(Nearest/Linear)Texture filter
-img.setWrap(Repeat/Clamp/...)Texture wrap

FBO

openFrameworks TrussC Notes
ofFboFbo
fbo.allocate(w, h)fbo.allocate(w, h)Same
fbo.begin()fbo.begin()Same
fbo.end()fbo.end()Same
fbo.draw(x, y)fbo.draw(x, y)Same
fbo.clear()fbo.clear()Same

Shader

openFrameworks TrussC Notes
ofShaderShader
shader.load(vert, frag)shader.load(vert, frag)Same
shader.begin()shader.begin()Same
shader.end()shader.end()Same
shader.setUniform1f(name, v)shader.setUniform(name, v)
shader.setUniformTexture(...)shader.setUniformTexture(...)Same

Mesh

openFrameworks TrussC Notes
ofMeshMesh
mesh.addVertex(v)mesh.addVertex(v)Same
mesh.addColor(c)mesh.addColor(c)Same
mesh.addTexCoord(t)mesh.addTexCoord(t)Same
mesh.addIndex(i)mesh.addIndex(i)Same
mesh.draw()mesh.draw()Same
mesh.setMode(OF_PRIMITIVE_...)mesh.setMode(PrimitiveType::...)

3D Primitives

openFrameworks TrussC Notes
ofPlanePrimitivecreatePlane(w, h)Returns Mesh
ofBoxPrimitivecreateBox(size)
ofSpherePrimitivecreateSphere(r, res)
ofIcoSpherePrimitivecreateIcoSphere(r, res)
ofCylinderPrimitivecreateCylinder(r, h, res)
ofConePrimitivecreateCone(r, h, res)

3D Camera

openFrameworks TrussC Notes
ofEasyCamEasyCam
ofCameraCamera
cam.begin()cam.begin()Same
cam.end()cam.end()Same
cam.setPosition(x, y, z)cam.setPosition(x, y, z)Same
cam.lookAt(target)cam.lookAt(target)Same

Lighting & Material

openFrameworks TrussC Notes
ofEnableLighting()enableLighting()
ofLightLight
ofMaterialMaterial
-Material::gold()Preset
-Material::silver()Preset

Sound

openFrameworks TrussC Notes
ofSoundPlayerSound
sound.load("file.wav")sound.load("file.wav")
sound.play()sound.play()
sound.stop()sound.stop()
sound.setVolume(v)sound.setVolume(v)
sound.setLoop(true)sound.setLoop(true)
ofSoundStreamSoundStreamAudio I/O

Video

openFrameworks TrussC Notes
ofVideoGrabberVideoGrabber
grabber.setup(w, h)grabber.setup(w, h)
grabber.update()grabber.update()
grabber.draw(x, y)grabber.draw(x, y)
grabber.isFrameNew()grabber.isFrameNew()

GUI

openFrameworks TrussC Notes
ofxGui / ofxImGuiImGui (built-in)Included by default
ofParameter<T>ImGui::SliderFloat etc.

I/O

openFrameworks TrussC Notes
ofSystemLoadDialog()systemLoadDialog()
ofSystemSaveDialog()systemSaveDialog()
ofLoadJson(path)loadJson(path)nlohmann/json
-loadXml(path)pugixml

Network

openFrameworks TrussC Notes
ofxTCPClientTcpClient
ofxTCPServerTcpServer
ofxUDPManagerUdpSocket
ofxOscOscReceiver / OscSender

Scene Graph

openFrameworks TrussC Notes
ofNodeNodeshared_ptr based
node.setPosition(x, y, z)node->setPosition(x, y, z)
node.setParent(parent)parent->addChild(child)
-RectNode2D UI, hit test support

Events

openFrameworks TrussC Notes
ofEvent<T>Event<T>
ofAddListener(...)EventListenerRAII style

Log

openFrameworks TrussC Notes
ofLog()tcLogNotice()
ofLogWarning()tcLogWarning()
ofLogError()tcLogError()

Thread

openFrameworks TrussC Notes
ofThreadstd::thread + MainThreadRunnerSafe sync
-MainThreadRunner::run(func)Execute on main thread

Serial

openFrameworks TrussC Notes
ofSerialSerial
serial.setup(port, baud)serial.setup(port, baud)Same
serial.readBytes(...)serial.readBytes(...)Same
serial.writeBytes(...)serial.writeBytes(...)Same

Timer

openFrameworks TrussC Notes
-node->callAfter(sec, func)Delayed execution
-node->callEvery(sec, func)Repeating execution

Ready to Start?

Your oF experience translates directly.