Main Components of the ArmorPaint Codebase: A Technical Architecture Guide
The ArmorPaint codebase is organized into six primary subsystems: a Kha/Kinc-based core rendering engine, a JavaScript UI layer, a Kong shader library, an asset pipeline for brushes and HDRs, optional C plugins, and a cross-platform build system using TinyCC.
ArmorPaint is a real-time PBR texture-painting application built on a hybrid architecture that combines low-level native code with high-level JavaScript orchestration. Understanding the main components of the ArmorPaint codebase is essential for developers looking to extend the software, optimize GPU rendering pipelines, or port the application to new platforms. The repository cleanly separates concerns between graphics abstraction, user interface logic, and asset management.
Core Rendering Engine
At the heart of the application lies a lightweight graphics layer built on Kha and Kinc, which handles all GPU work including shaders, render passes, and frame buffers.
The native engine code resides primarily in paint/sources/viewport.c, which implements the main viewport loop responsible for window creation, input handling, and driving the real-time preview. This C-based layer abstracts platform-specific graphics APIs (Vulkan, Direct3D 12, Metal) through the Kinc backend, ensuring consistent behavior across Windows, Linux, macOS, and mobile targets.
Rendering effects are implemented via Kong shaders—a Kha-specific shader format with the .kong extension. Key render passes include:
paint/shaders/world_pass.kong– Handles environmental lighting and world-space calculationspaint/shaders/ssao_pass.kong– Implements screen-space ambient occlusionpaint/shaders/deferred_light.kong– Manages deferred lighting calculations for the final composite
These shaders are invoked from the engine's main loop and operate on GPU textures and frame buffers allocated through the Kha API.
UI and Application Logic
A thin JavaScript layer serves as the glue between the native engine and the HTML-based user interface. This layer manages project serialization, brush handling, tool panels, and high-level application commands.
The entry point is paint/project.js, which instantiates the Kha Engine class and initializes the painting environment:
// paint/project.js – simplified entry point
import { Engine } from "kha/engine";
import { loadProject } from "./project_loader.js";
const engine = new Engine({
width: 1280,
height: 720,
title: "ArmorPaint"
});
engine.start(() => {
// Load an empty project or a saved .arm file
loadProject(engine, "paint/assets/default_brush.arm");
});
This JavaScript orchestration layer communicates with the native viewport through Kha bindings, translating UI events (brush strokes, color picks) into GPU commands while managing the application state.
Shader Library
The Kong shader library in paint/shaders/ provides the visual effects that power ArmorPaint's real-time PBR preview. Unlike traditional GLSL or HLSL, these .kong files use Kha's cross-platform shading language that compiles to the appropriate backend (SPIR-V for Vulkan, HLSL for DirectX, MSL for Metal).
Critical rendering passes include:
- Deferred lighting (
deferred_light.kong) – Calculates physically based lighting from G-buffer data - Post-processing effects (
bloom_upsample_pass.kong,ssao_pass.kong) – Add screen-space ambient occlusion and bloom
You can invoke these passes manually through the JavaScript API when extending the renderer:
import { ShaderPass } from "kha/shaders";
function renderSSAO(gbuffer, output) {
const ssaoPass = new ShaderPass("paint/shaders/ssao_pass.kong");
ssaoPass.setTexture("gDepth", gbuffer.depth);
ssaoPass.setTexture("gNormal", gbuffer.normal);
ssaoPass.render(output);
}
Asset Pipeline
The paint/assets/ directory stores the binary resources required at runtime, including PNG textures, HDR environment maps, and proprietary .arm files that bundle brush data and project settings.
Key asset types include:
- Brushes (
default_brush.arm) – Serialized brush presets containing stroke dynamics and texture masks - Environment lighting (
World_radiance.hdr) – High-dynamic-range skyboxes used for image-based lighting in the PBR viewport - Cursor graphics – Custom mouse cursors for different painting tools
These assets are either bundled with the application binary or loaded dynamically through the loadProject() function exposed in paint/project.js.
Plugin System
ArmorPaint supports optional C-based plugins that extend core functionality without modifying the main codebase. These plugins compile into the binary using the included Tiny C Compiler (TCC) and expose JavaScript bindings through the Kha native extension API.
A representative example is the UV unwrap tool located in paint/plugins/uv_unwrap/uv_unwrap.c. This plugin implements the UV unwrap algorithm in native C for performance, then exposes it to the JavaScript layer:
import { UVUnwrap } from "paint/plugins/uv_unwrap/uv_unwrap";
function unwrapSelectedMesh(mesh) {
const result = UVUnwrap.unwrap(mesh);
mesh.setUVs(result.uvs);
}
The C implementation handles the computational geometry, while the JavaScript wrapper manages input validation and UI updates.
Build and Tooling Layer
The base/ directory contains the infrastructure required to compile ArmorPaint across supported platforms. The build system uses platform-specific driver scripts rather than a meta-build system like CMake.
Key components include:
base/makeandbase/make.bat– Shell scripts that drive compilation for Windows, Linux, macOS, Android, iOS, and WebAssembly targetsbase/tools/tcc/– Contains the Tiny C Compiler (main.c) used specifically for compiling the internal plugin systembase/docs/– Platform-specific dependency documentation (e.g.,linux_deps.mdfor Ubuntu/Arch build requirements)
The build scripts handle Kinc project generation, shader compilation, and asset bundling, producing standalone binaries that include both the native engine and the JavaScript runtime.
Summary
- Core Rendering Engine – Native C code in
paint/sources/viewport.cand Kong shaders handle all GPU operations through the Kha/Kinc abstraction layer - UI Layer – JavaScript orchestration in
paint/project.jsmanages the application state and bridges the native engine with the HTML interface - Shader Library –
.kongfiles inpaint/shaders/implement deferred lighting, SSAO, and post-processing effects using Kha's cross-platform shading language - Asset Pipeline – Binary resources including
.armbrush files and HDR environment maps reside inpaint/assets/ - Plugin System – Optional C extensions like
uv_unwrap.ccompile via TCC and extend functionality through JavaScript bindings - Build System – Platform-specific scripts in
base/make*and the TinyCC toolchain handle cross-platform compilation
Frequently Asked Questions
What graphics API does ArmorPaint use under the hood?
ArmorPaint uses the Kinc framework to abstract graphics APIs, automatically targeting Vulkan on Linux, Direct3D 12 on Windows, Metal on macOS/iOS, and OpenGL/WebGL for web builds. The actual rendering commands are written in C in paint/sources/viewport.c, while shaders are authored in Kong format and cross-compiled to the appropriate backend.
How does the JavaScript layer communicate with the native C engine?
The JavaScript code in paint/project.js and related modules communicates with native code through Kha bindings. Kha generates JavaScript wrapper functions that marshal calls into the native C layer, allowing the UI to trigger GPU operations like shader passes or viewport updates without direct pointer manipulation.
Where are custom brushes and textures stored in the codebase?
Default brushes ship as .arm files in paint/assets/default_brush.arm, while user-created brushes and projects serialize to the same format at runtime. These files bundle brush dynamics, alpha masks, and metadata. HDR environment maps for lighting are stored as paint/assets/World_radiance.hdr and similar files.
Can I extend ArmorPaint without modifying the core engine?
Yes. The plugin system allows you to write C extensions in paint/plugins/ that compile via the included Tiny C Compiler (base/tools/tcc/main.c). These plugins expose JavaScript APIs and can implement complex algorithms like UV unwrapping without requiring changes to viewport.c or the main rendering loop.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →