Add a HeadPaint tool
Add a custom painting tool using HeadPaint's supported extension interface.
Let’s add a single-pixel brush to HeadPaint. It receives pixel coordinates from the editor, paints with the current color, and reports which part of the texture changed. The example demonstrates extending a tool without adding a custom layer or history system.
Download the complete headpaint-tool.js example. After installation, open HeadPaint and select the “Sample pixel” tool with the pencil icon.
Define the tool’s task
The brush paints one pixel under the pointer. The base layer is always opaque;
the outer layer uses HeadPaint’s current alpha. In replace mode, the outer-layer
pixel is cleared before the new color is applied. In other modes, the example uses
the standard Canvas source-over compositing operation.
The built-in brush’s size and shape, smart grid, and special blend modes are not automatically applied to a custom handler. The example deliberately performs one specific action. If you need those settings, implement their behavior separately.
Check the available API
The public extension point is window.editorAPI.headPaint.addTool(tool). The editor
accepts an object with a unique id, label, icon, and synchronous onPaint handler.
There is no registerBrush() method, and onPaint is not a method of the API itself.
const paint = window.editorAPI?.headPaint;if (typeof paint?.addTool !== 'function') { throw new Error('The HeadPaint API is unavailable.');}The method adds a tool to HeadPaint’s set but does not enable painting mode or select
the tool for the user. There is currently no public removeTool() method. Registering
the same id again replaces its description in the list; a different id creates
a separate tool. addTool() does not return a removal handle.
All fields and restrictions are listed in the HeadPaint API reference.
Register the tool and handler
A shortened example that paints an opaque pixel:
window.editorAPI.headPaint.addTool({ id: 'my-plugin.pixel', name: 'My pixel', icon: 'icon-pencil', onPaint({context, drawX, drawY, color}) { context.save(); context.globalAlpha = 1; context.globalCompositeOperation = 'source-over'; context.fillStyle = color; context.fillRect(drawX, drawY, 1, 1); context.restore(); return [{drawX, drawY}]; },});Use drawX and drawY for Canvas coordinates: the editor has already accounted
for the texture’s vertical direction and the selected layer. Do not replace them
directly with the surface hit coordinates x and y. Paint only within
canvas.width and canvas.height.
The handler changes pixels through context. The returned array of coordinates does
not paint for you; it tells the editor which locations changed. Return false when
nothing changed. For any other return value, the editor treats the current pixel as
changed. Coordinates already processed during the gesture are remembered, so this
handler is not designed to build up color at the same position with every mouse movement.
onPaint is called synchronously. Do not make it async or load images during
a stroke; prepare data in advance. Once registered, the tool is selected with its
normal HeadPaint button. The optional onSelect receives internal editor and
headPaint objects, but this example does not use them.
The complete file also preserves Canvas state with save()/restore(), handles outer
layer alpha, and compares the pixel before and after painting to return false
when there is no change.
Color, alpha, and the palette
The handler receives color in #RRGGBB format and opacity in the range 0-255.
The public method lets you change the current color and, optionally, its alpha:
window.editorAPI.headPaint.setColor('#f90', 128);Short HEX values are expanded and the color is stored in uppercase. Numeric alpha
is rounded and clamped to 0-255. Without the second argument, the current alpha
is preserved. An invalid color or nonnumeric alpha is ignored.
setUpdateColorList(false) changes the palette-update flag where built-in tools
check it; true restores that behavior. The method does not clear the palette or add
the supplied color to it. The custom onPaint path does not automatically populate
the built-in palette. The example leaves the user’s flag unchanged.
Layers and texture preparation are explained in painting in HeadPaint.
Check layers, Undo, and cleanup
- Create a separate test head. Draw a short stroke on the base layer: pixels should receive the selected color at full opacity.
- Switch to the outer layer and reduce alpha. Check normal compositing and pixel replacement; painting near an edge must not affect the adjacent layer.
- Select the built-in brush, then return to the sample tool. Check that each button selects its own action.
- Reinstall the file with the same namespace and
id, then reload the editor. There should be one button for the example. - Delete the plugin and reload the editor to remove its registration.
The current custom tool path sends bde:headPaint:paint notifications and updates
the texture, but does not set the history change marker used by the built-in brush.
A paint event does not imply a started/ended pair or Undo support. The example
does not work around this by accessing the internal history command; such access
would introduce separate compatibility risks.
The contract was checked against publicFunctions.js, gui/headPaint.js, and
commands/headPaint.js. Before releasing your own tool, test layers and history in
the editor version you use. Tool registration alone does not verify those behaviors.