# Main Components of the ArmorPaint Codebase: A Technical Architecture Guide

> Explore the main components of the ArmorPaint codebase. Discover its core rendering engine, JavaScript UI, shader library, asset pipeline, C plugins, and build system. A technical architecture guide for developers.

- Repository: [Armory 3D/armorpaint](https://github.com/armory3d/armorpaint)
- Tags: architecture
- Published: 2026-09-13

---

**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`](https://github.com/armory3d/armorpaint/blob/main/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 calculations
- `paint/shaders/ssao_pass.kong` – Implements screen-space ambient occlusion
- `paint/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`](https://github.com/armory3d/armorpaint/blob/main/paint/project.js), which instantiates the Kha `Engine` class and initializes the painting environment:

```javascript
// 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:

```javascript
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`](https://github.com/armory3d/armorpaint/blob/main/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`](https://github.com/armory3d/armorpaint/blob/main/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:

```javascript
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/make` and `base/make.bat`** – Shell scripts that drive compilation for Windows, Linux, macOS, Android, iOS, and WebAssembly targets
- **`base/tools/tcc/`** – Contains the Tiny C Compiler ([`main.c`](https://github.com/armory3d/armorpaint/blob/main/main.c)) used specifically for compiling the internal plugin system
- **`base/docs/`** – Platform-specific dependency documentation (e.g., [`linux_deps.md`](https://github.com/armory3d/armorpaint/blob/main/linux_deps.md) for 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.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/viewport.c) and Kong shaders handle all GPU operations through the Kha/Kinc abstraction layer
- **UI Layer** – JavaScript orchestration in [`paint/project.js`](https://github.com/armory3d/armorpaint/blob/main/paint/project.js) manages the application state and bridges the native engine with the HTML interface
- **Shader Library** – `.kong` files in `paint/shaders/` implement deferred lighting, SSAO, and post-processing effects using Kha's cross-platform shading language
- **Asset Pipeline** – Binary resources including `.arm` brush files and HDR environment maps reside in `paint/assets/`
- **Plugin System** – Optional C extensions like [`uv_unwrap.c`](https://github.com/armory3d/armorpaint/blob/main/uv_unwrap.c) compile 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`](https://github.com/armory3d/armorpaint/blob/main/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`](https://github.com/armory3d/armorpaint/blob/main/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`](https://github.com/armory3d/armorpaint/blob/main/base/tools/tcc/main.c)). These plugins expose JavaScript APIs and can implement complex algorithms like UV unwrapping without requiring changes to [`viewport.c`](https://github.com/armory3d/armorpaint/blob/main/viewport.c) or the main rendering loop.