# How Brushes Are Implemented and Managed in ArmorPaint

> Discover how ArmorPaint implements and manages brushes using JSON asset definitions and a BrushManager for real-time rendering and synchronized GPU uniforms.

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

---

**TLDR:** ArmorPaint implements brushes as JSON-serialized asset definitions that are loaded into a runtime `Brush` class, coordinated by a singleton `BrushManager` that synchronizes settings with GPU uniforms, while specialized UI panels and painting shaders consume this data to render strokes in real time.

In the **ArmorPaint** repository, the brush system forms the critical bridge between user input and GPU-accelerated texture painting. Understanding how brushes are implemented and managed in ArmorPaint requires examining the relationship between declarative asset definitions, JavaScript runtime classes, and shader pipeline integration.

## Brush Asset Definitions in JSON

ArmorPaint stores brush presets as **JSON asset files** within the `paint/assets/brushes/` directory. Each definition describes the tip shape, radius, hardness, strength, smoothing, tilt behavior, and texture mask references. At application startup, the engine deserializes these files to instantiate runtime brush objects.

Example structure from [`paint/assets/brushes/default_brush.json`](https://github.com/armory3d/armorpaint/blob/main/paint/assets/brushes/default_brush.json):

```json
{
  "name": "Soft Round",
  "radius": 30,
  "hardness": 0.2,
  "strength": 0.8,
  "tilt": true,
  "texture": "brush_tip_soft.png"
}

```

The JSON schema supports custom presets that define mathematical properties consumed by both the JavaScript logic and the GPU shaders.

## The Brush Class and Runtime State

The `Brush` class defined in [`paint/sources/brush/brush.js`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/brush/brush.js) encapsulates the active brush state and behavior. It holds current parameters in memory, provides methods for sampling the brush tip texture, and exports data structures required by the painting pipelines for stroke calculation, masking, and normal manipulation.

```js
import { Brush } from "./brush/brush.js";

// Instantiate from loaded JSON data
const eraserBrush = new Brush({
  name: "Hard Eraser",
  radius: 50,
  hardness: 1.0,
  strength: 1.0,
  tilt: false,
  texture: "brush_tip_hard.png"
});

```

This class acts as the data model, separating the asset definition from the runtime execution context.

## BrushManager Singleton Coordination

The `BrushManager` singleton in [`paint/sources/brush/brush_manager.js`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/brush/brush_manager.js) serves as the central authority for brush lifecycle and state management. It maintains the registry of available presets, tracks the active `Brush` instance, reacts to UI parameter changes, and handles GPU synchronization by updating uniform buffers.

When users select a different preset or modify settings, the manager swaps the active brush instance and pushes updates to the graphics pipeline:

```js
import { BrushManager } from "./brush/brush_manager.js";

// Register preset for UI dropdown
BrushManager.registerPreset(eraserBrush);

// UI callback when user selects a brush
function onBrushSelect(presetName) {
  const brush = BrushManager.getPreset(presetName);
  BrushManager.setActiveBrush(brush);
  // Automatically updates u_brushData uniform buffer
}

```

The manager exposes events that rendering and shader systems subscribe to, ensuring zero-latency updates when brush properties change mid-stroke.

## UI Integration via BrushPanel

The interface layer accesses brush data through the `BrushPanel` class located in [`paint/sources/ui/brush_panel.js`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/ui/brush_panel.js). This panel reads current values from the `BrushManager`, renders interactive sliders for radius and strength, and writes changes back to the active brush instance. When parameters update, the panel triggers a refresh of the brush preview texture without requiring a full asset reload.

## GPU Pipeline and Shader Consumption

The painting shaders consume brush data through two critical uniforms defined in `paint/shaders/brush_shader.kong`. The sampler `u_brushTex` provides the brush tip alpha mask, while `u_brushData` supplies a **vec4** containing the radius, hardness, and strength calculations for the current stroke.

```glsl
uniform sampler2D u_brushTex;
uniform vec4 u_brushData; // xy = radius, zw = hardness/strength

vec4 sampleBrush(in vec2 uv) {
  float dist = length(uv) * u_brushData.x;
  float mask = texture(u_brushTex, uv).r;
  float falloff = smoothstep(1.0, u_brushData.y, dist);
  return vec4(mask * falloff * u_brushData.z);
}

```

During each paint operation, the shader samples the brush tip texture, applies the falloff calculation based on the radius and hardness parameters, and multiplies by the strength value to determine the final contribution to the target layer.

## Summary

- **ArmorPaint** serializes brush definitions as JSON assets in `paint/assets/brushes/` that describe mathematical and textural properties
- The `Brush` class in [`paint/sources/brush/brush.js`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/brush/brush.js) represents the runtime state and provides texture sampling methods
- `BrushManager` in [`paint/sources/brush/brush_manager.js`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/brush/brush_manager.js) coordinates the active brush, maintains the preset registry, and synchronizes GPU uniform buffers
- UI interaction flows through `BrushPanel` in [`paint/sources/ui/brush_panel.js`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/ui/brush_panel.js) to update brush parameters in real time
- The shader pipeline in `paint/shaders/brush_shader.kong` consumes `u_brushTex` and `u_brushData` uniforms to calculate stroke contributions per fragment

## Frequently Asked Questions

### Where are brush presets stored in the ArmorPaint repository?

Brush presets are stored as individual JSON files in the `paint/assets/brushes/` directory. Each file contains the name, radius, hardness, strength, tilt support flag, and texture path that the `Brush` class parses during initialization.

### How does ArmorPaint update brush settings when I adjust the UI sliders?

When you modify sliders in the brush panel, the `BrushPanel` class writes new values to the `BrushManager` singleton, which immediately updates the GPU uniform buffer `u_brushData` and refreshes the bound texture reference. This occurs without application restart, ensuring the next stroke reflects the new settings instantly.

### Can I create custom brushes by editing JSON files directly?

Yes. You can create custom brushes by adding new JSON files to the brushes asset folder following the established schema. The `Brush` constructor automatically parses these definitions at runtime, and `BrushManager.registerPreset()` makes them selectable in the UI dropdown without requiring code changes.

### Which shader uniform controls the brush tip shape during painting?

The `u_brushTex` sampler2D uniform controls the brush tip alpha mask, while `u_brushData` supplies the mathematical parameters (radius, hardness, strength) used to calculate falloff and intensity in the fragment shader located at `paint/shaders/brush_shader.kong`.