For openFrameworks Users
If you love oF, you'll love TrussC.
Same joy, modern tech.
oF users will feel right at home
The fundamental rhythm of creative coding. This structure is unchanged.
ofApp
→
tcApp
We kept method names you know and love to minimize the learning curve.
ofDrawCircle(x, y, r)
→
drawCircle(x, y, r)
It's the standard now. Cursor user? Of course that works too.
TrussC's answers to long-standing oF frustrations
ofSetLineWidth() isn't guaranteed above 1px due to OpenGL limitations.
On macOS / Metal, it's completely ignored. Drawing thick lines meant generating meshes manually.
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();
ofNode exists but parent-child relationships require manual management.
Memory management gets complex when adding/removing children.
Children can outlive their parents, causing issues.
// 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
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.
// 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
}
}
};
Hit testing overlapping objects is cumbersome.
Z-order aware hit testing had to be implemented from scratch.
// 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();
}
ofThread is prone to data races.
No standard way to safely "execute after N seconds".
Time-checking in update() leads to messy code.
// 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
});
Apple deprecated OpenGL in favor of Metal.
Future compatibility and performance optimization became uncertain.
The looming question: when will support be dropped?
Metal / DirectX 12 / Vulkan / WebGPU support.
Runs on OS-recommended native APIs for optimal speed and power efficiency.
WebAssembly + WebGPU provides excellent browser portability.
oF itself is MIT, but dependencies include GPL libraries.
Checking licenses for FFmpeg, FreeType, etc. is tedious.
Uncertainty when using for client work.
Zero GPL contamination guaranteed.
All dependencies are commercial-use friendly.
Use with confidence for client projects.
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).
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
oF functions → TrussC equivalents
| openFrameworks | TrussC | Notes |
|---|---|---|
ofApp : public ofBaseApp | tcApp : 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() |
| 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) | |
ofPath | Path | |
- | StrokeMesh | Thick lines with proper edges |
ofEnableBlendMode(...) | setBlendMode(...) | |
glScissor(...) | setScissor(...) |
| 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) |
| openFrameworks | TrussC | Notes |
|---|---|---|
glm::vec2 / ofVec2f | Vec2 | |
glm::vec3 / ofVec3f | Vec3 | |
glm::vec4 / ofVec4f | Vec4 | |
glm::mat4 / ofMatrix4x4 | Mat4 | |
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) | |
PI | PI | |
TWO_PI | TAU | τ = 2π |
| 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 |
| openFrameworks | TrussC | Notes |
|---|---|---|
ofTrueTypeFont | Font | |
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 |
| openFrameworks | TrussC | Notes |
|---|---|---|
ofImage | Image | |
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 |
| openFrameworks | TrussC | Notes |
|---|---|---|
ofFbo | Fbo | |
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 |
| openFrameworks | TrussC | Notes |
|---|---|---|
ofShader | Shader | |
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 |
| openFrameworks | TrussC | Notes |
|---|---|---|
ofMesh | Mesh | |
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::...) |
| openFrameworks | TrussC | Notes |
|---|---|---|
ofPlanePrimitive | createPlane(w, h) | Returns Mesh |
ofBoxPrimitive | createBox(size) | |
ofSpherePrimitive | createSphere(r, res) | |
ofIcoSpherePrimitive | createIcoSphere(r, res) | |
ofCylinderPrimitive | createCylinder(r, h, res) | |
ofConePrimitive | createCone(r, h, res) |
| openFrameworks | TrussC | Notes |
|---|---|---|
ofEasyCam | EasyCam | |
ofCamera | Camera | |
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 |
| openFrameworks | TrussC | Notes |
|---|---|---|
ofEnableLighting() | enableLighting() | |
ofLight | Light | |
ofMaterial | Material | |
| - | Material::gold() | Preset |
| - | Material::silver() | Preset |
| openFrameworks | TrussC | Notes |
|---|---|---|
ofSoundPlayer | Sound | |
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) | |
ofSoundStream | SoundStream | Audio I/O |
| openFrameworks | TrussC | Notes |
|---|---|---|
ofVideoGrabber | VideoGrabber | |
grabber.setup(w, h) | grabber.setup(w, h) | |
grabber.update() | grabber.update() | |
grabber.draw(x, y) | grabber.draw(x, y) | |
grabber.isFrameNew() | grabber.isFrameNew() |
| openFrameworks | TrussC | Notes |
|---|---|---|
ofxGui / ofxImGui | ImGui (built-in) | Included by default |
ofParameter<T> | ImGui::SliderFloat etc. |
| openFrameworks | TrussC | Notes |
|---|---|---|
ofSystemLoadDialog() | systemLoadDialog() | |
ofSystemSaveDialog() | systemSaveDialog() | |
ofLoadJson(path) | loadJson(path) | nlohmann/json |
| - | loadXml(path) | pugixml |
| openFrameworks | TrussC | Notes |
|---|---|---|
ofxTCPClient | TcpClient | |
ofxTCPServer | TcpServer | |
ofxUDPManager | UdpSocket | |
ofxOsc | OscReceiver / OscSender |
| openFrameworks | TrussC | Notes |
|---|---|---|
ofNode | Node | shared_ptr based |
node.setPosition(x, y, z) | node->setPosition(x, y, z) | |
node.setParent(parent) | parent->addChild(child) | |
| - | RectNode | 2D UI, hit test support |
| openFrameworks | TrussC | Notes |
|---|---|---|
ofEvent<T> | Event<T> | |
ofAddListener(...) | EventListener | RAII style |
| openFrameworks | TrussC | Notes |
|---|---|---|
ofLog() | tcLogNotice() | |
ofLogWarning() | tcLogWarning() | |
ofLogError() | tcLogError() |
| openFrameworks | TrussC | Notes |
|---|---|---|
ofThread | std::thread + MainThreadRunner | Safe sync |
| - | MainThreadRunner::run(func) | Execute on main thread |
| openFrameworks | TrussC | Notes |
|---|---|---|
ofSerial | Serial | |
serial.setup(port, baud) | serial.setup(port, baud) | Same |
serial.readBytes(...) | serial.readBytes(...) | Same |
serial.writeBytes(...) | serial.writeBytes(...) | Same |
| openFrameworks | TrussC | Notes |
|---|---|---|
| - | node->callAfter(sec, func) | Delayed execution |
| - | node->callEvery(sec, func) | Repeating execution |
Your oF experience translates directly.