# How to Implement Custom Plugins in ArmorPaint: A Complete Developer Guide

> Learn to implement custom plugins in ArmorPaint with this developer guide. Discover how to add new features using MiniC scripts and harness the power of the plugin system.

- Repository: [Armory 3D/armorpaint](https://github.com/armory3d/armorpaint)
- Tags: how-to-guide
- Published: 2026-09-11

---

**ArmorPaint implements a lightweight plugin system where custom functionality is added by placing MiniC script files in the `plugins/` folder and registering callbacks via `plugin_create()` and `plugin_notify_on_*()` functions.**

ArmorPaint's plugin architecture is deliberately minimal yet powerful, allowing developers to extend the 3D painting software without recompiling the engine. According to the armory3d/armorpaint source code, the system uses an embedded MiniC interpreter to evaluate plugin scripts stored in the repository's `plugins/` directory. This guide explains how to implement custom plugins in ArmorPaint by leveraging the core API found in [`paint/sources/plugin.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/plugin.c) and configuration management in [`paint/sources/config.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/config.c).

## How the ArmorPaint Plugin System Works

ArmorPaint's plugin system operates through a straightforward callback-based architecture. When you implement custom plugins in ArmorPaint, you create MiniC scripts that define a `plugin_t` object and register function pointers for UI updates, game tick updates, and cleanup operations.

The engine maintains a global map `g_plugins` that stores active plugin handles, with lifecycle management handled by `plugin_start()` and `plugin_stop()` in [`paint/sources/plugin.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/plugin.c).

### The Five-Stage Plugin Lifecycle

Understanding the execution flow is essential for stable plugin development:

1. **Script Loading**: `plugin_start()` constructs the path `plugins/<name>`, reads the file into a blob, and executes it via `minic_eval_named(blob, name)` at lines 13-22 in [`paint/sources/plugin.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/plugin.c).

2. **Object Creation**: Inside the script, `plugin_create()` allocates a `plugin_t` structure, registers the plugin name in `g_plugins`, and returns a handle (lines 6-10 in [`paint/sources/plugin.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/plugin.c)).

3. **Callback Registration**: The script registers event handlers using `plugin_notify_on_ui()`, `plugin_notify_on_update()`, and `plugin_notify_on_delete()`, which store function pointers in the `plugin_t` struct (lines 43-53 in [`paint/sources/plugin.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/plugin.c)).

4. **Execution**: During the main UI loop in [`paint/sources/base.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/base.c), ArmorPaint iterates through `g_plugins`, invoking `on_ui` during frame rendering, `on_update` each tick, and `on_delete` during unload.

5. **Cleanup**: `plugin_stop()` looks up the plugin, executes the `on_delete` callback if present, frees the MiniC context, and removes the entry from the global map (lines 30-41 in [`paint/sources/plugin.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/plugin.c)).

## Creating Your First ArmorPaint Plugin

To implement custom plugins in ArmorPaint, create a MiniC file in the `plugins/` directory. Here is a minimal "Hello World" implementation that adds a UI button and logs messages:

```c
/* File: plugins/hello.c */
/* 1. Create the plugin object */
var plug = plugin_create();

/* 2. Register a UI callback */
plugin_notify_on_ui(plug, function() {
    // Add a button to the side panel
    var btn = ui_button("Say Hello");
    if (ui_clicked(btn)) {
        console_log("Hello from my custom plugin!");
    }
});

/* 3. Register an update callback (runs every tick) */
plugin_notify_on_update(plug, function() {
    // Simple per-frame log (remove in production!)
    console_log("Tick …");
});

/* 4. Optional cleanup when disabled */
plugin_notify_on_delete(plug, function() {
    console_log("Good-bye from hello plugin");
});

```

Save this code as [`plugins/hello.c`](https://github.com/armory3d/armorpaint/blob/main/plugins/hello.c). The `plugin_create()` function initializes the plugin context, while `plugin_notify_on_ui()` injects custom interface elements into ArmorPaint's side panel.

### Enabling Your Plugin

Enable your implementation through the Settings interface or programmatically:

1. Open **Settings → Plugins** in ArmorPaint
2. Click **Enable** and type `hello` (the filename without extension)
3. The engine calls `config_enable_plugin()` in [`paint/sources/config.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/config.c) (lines 78-89), which pushes the name to `g_config->plugins` and invokes `plugin_start()`

The UI button appears immediately in the side panel, and the console displays "Tick …" every frame until disabled.

## Advanced Plugin Development Patterns

For production implementations, study the reference UV Unwrap plugin located at [`paint/plugins/uv_unwrap/uv_unwrap.c`](https://github.com/armory3d/armorpaint/blob/main/paint/plugins/uv_unwrap/uv_unwrap.c). This demonstrates how to implement custom plugins in ArmorPaint that interact with the engine's mesh processing API.

The UV Unwrap plugin calls `proc_uv_unwrap()` and manipulates material slots, showing how to access core painting functionality beyond simple UI elements. Key techniques include:

- **Mesh Processing**: Call engine procedures like `proc_uv_unwrap()` directly from MiniC callbacks
- **State Management**: Store persistent data in global script variables between update calls
- **Conditional UI**: Use `ui_clicked()` and related functions to handle user input events

When implementing complex tools, register only the callbacks you need. If your plugin performs background processing without UI elements, omit `plugin_notify_on_ui()` to reduce frame overhead.

## Managing Plugin Lifecycle Programmatically

The configuration system in [`paint/sources/config.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/config.c) provides functions for dynamic plugin management:

- **`config_enable_plugin(name)`**: Adds the plugin to the user configuration and triggers `plugin_start()`
- **`config_disable_plugin(name)`**: Removes the plugin from `g_config->plugins` and calls `plugin_stop()`

These functions are useful for creating plugin managers or dependency systems where one plugin enables others based on project requirements.

## Summary

- ArmorPaint uses a file-based plugin system where MiniC scripts placed in `plugins/` are evaluated by the MiniC interpreter at runtime
- **`plugin_create()`** in [`paint/sources/plugin.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/plugin.c) allocates the `plugin_t` object and registers it in the global `g_plugins` map
- **Callbacks** are registered via `plugin_notify_on_ui()`, `plugin_notify_on_update()`, and `plugin_notify_on_delete()` to hook into the main application loop
- **`plugin_start()`** and **`plugin_stop()`** handle script evaluation and cleanup, including MiniC context management at lines 13-22 and 30-41 respectively
- Enable plugins through **Settings → Plugins** or programmatically via `config_enable_plugin()` in [`paint/sources/config.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/config.c)
- Reference the UV Unwrap plugin at [`paint/plugins/uv_unwrap/uv_unwrap.c`](https://github.com/armory3d/armorpaint/blob/main/paint/plugins/uv_unwrap/uv_unwrap.c) for advanced mesh processing examples

## Frequently Asked Questions

### What programming language do ArmorPaint plugins use?

ArmorPaint plugins are written in MiniC, a lightweight C-like scripting language interpreted at runtime. The engine evaluates plugin scripts using `minic_eval_named()` as implemented in [`paint/sources/plugin.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/plugin.c), allowing immediate execution without compilation or engine recompilation.

### Where should I place my custom plugin files?

Place your MiniC script files in the `plugins/` folder within the ArmorPaint directory. The `plugin_start()` function constructs paths using the pattern `plugins/<name>` and automatically loads the script, as defined in [`paint/sources/plugin.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/plugin.c) lines 13-22.

### How do I add buttons or UI elements to ArmorPaint?

Register a UI callback using `plugin_notify_on_ui(plugin_handle, function)`. Inside the callback function, use UI construction functions like `ui_button()` and `ui_clicked()` to create interactive elements. The engine invokes this callback during every UI frame in the main loop defined in [`paint/sources/base.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/base.c).

### Can plugins access ArmorPaint's mesh and texture data?

Yes. Advanced plugins like the UV Unwrap example demonstrate access to the engine's procedural mesh API through functions like `proc_uv_unwrap()`. Plugins can interact with material slots, mesh data, and painting operations by calling the engine's exposed C functions from MiniC callbacks.