Use this file to discover all available pages before exploring further.
milsymbol was built on the principle that everything used internally is accessible externally. Every symbol part, icon fragment, label placement rule, and color lookup table is a modular piece you can inspect, override, and extend. This makes it possible to add new tactical symbols, replace built-in icons, reposition text fields for special SIDCs, and write reusable symbol features — all without forking the library.
Symbol parts are the core extension primitive in milsymbol. Each part is a function that runs during symbol rendering and can contribute draw instructions that appear either before (pre) or after (post) the main symbol parts. A part also contributes a bounding box that is merged into the symbol’s overall bounds to ensure the canvas is large enough to include the new elements.The function receives the ms namespace as its argument and is called with this bound to the symbol instance, giving access to this.options, this.style, this.metadata, this.colors, and this.bbox.
ms.addSymbolPart(function myExtension(ms) { const options = this.getOptions(); const bbox = new ms.BBox(); const preDrawArray = []; const postDrawArray = []; // some logic using this.options, this.metadata, etc. return { pre: preDrawArray, post: postDrawArray, bbox };});
pre — Draw instructions in this array are rendered before all built-in symbol parts. Use pre for background elements (e.g. a colored glow or shadow) that must appear underneath the frame and icon.
post — Draw instructions in this array are rendered after all built-in symbol parts. Use post for overlays, labels, or decorations that must appear on top.
Icons in milsymbol are assembled from reusable icon parts — small draw instructions keyed by a name string. For example, the infantry icon and the armor icon are separate parts that are combined to form the armored infantry icon. By adding new icon parts you can define reusable drawing primitives for custom SIDCs.The callback you register has the signature (iconParts, metadata, colors) and is called with this bound to the symbol instance:
ms.addIconParts( function (iconParts, metadata, colors) { /* iconParts: Object — The existing icon parts registry metadata: Object — Symbol metadata (affiliation, dimension, echelon, etc.) colors: Object — Color object for this symbol */ // Add a custom tactical graphic harbor icon iconParts["TP.HARBOR"] = { type: "path", fill: false, d: "M 80,140 50,60 150,60 120,140" }; // Since we're modifying the existing object directly, no return needed. });
Icon part names can be any string. By convention, custom parts that are not part of the standard library are prefixed (e.g. "TP." for tactical points) to avoid collisions with built-in parts.
ms.addSIDCicons() maps SIDC codes to icon parts and their bounding boxes. You can register entirely new SIDCs or override existing ones. The function receives the current SIDC map and bounding box map, and the std2525 flag.
ms.addSIDCicons( function tacticalPoints(sidc, bbox, icn, std2525) { /* sidc: Object — Existing SIDC → draw instruction map bbox: Object — Existing SIDC → bounding box map icn: Object — Existing icon parts registry std2525: Boolean — Is it 2525 (true) or APP-6 (false) */ // Create a new SIDC using an existing icon part sidc["G-T-D-----"] = icn["TP.DESTROY"]; bbox["G-T-D-----"] = { x1: 0, x2: 200, y1: 40, y2: 160 }; // Without a bounding box it defaults to the icon octagon bounds sidc["G-T-I-----"] = icn["TP.INTERDICT"]; // No return needed — modifying the objects directly }, "letter" // 'letter' for letter-based SIDCs, 'number' for number-based);
The type parameter ("letter" or "number") tells milsymbol which SIDC lookup table to update. Letter-based SIDCs use hyphenated codes like "G-T-D-----". Number-based SIDCs are 15+ digit numeric strings.
For symbols where the standard text field positions don’t work — especially tactical graphics — you can register custom label placement rules for specific SIDCs. Each rule maps a modifier option name to a text draw instruction override.
Each key in the override object matches a modifier option name (e.g. uniqueDesignation, dtg). The value is a partial text draw instruction — any property listed here overrides the default. The x, y, and fontsize values use the same coordinate space as the draw instructions (origin at 100, 100; octagon width = 100 px).
The dtg1 key is a special suffix variant: when two DTG values need to be displayed on separate lines, the second line can be targeted as dtg1. This pattern lets you position multi-line field content independently.
When writing milsymbol extensions in TypeScript, you can add your custom options to the SymbolOptions interface via module augmentation so the type checker accepts your new properties.
For advanced scenarios where you need to replace or reorder the entire pipeline of symbol parts, use getSymbolParts() and setSymbolParts().
// Inspect the current partsvar parts = ms.getSymbolParts();console.log(parts.length); // number of registered parts// Replace all parts (e.g. remove all built-in parts and start fresh)ms.setSymbolParts([myCustomPart1, myCustomPart2]);// Insert a part at a specific position in the pipelinevar existingParts = ms.getSymbolParts();existingParts.splice(2, 0, myNewPart); // insert at index 2ms.setSymbolParts(existingParts);
setSymbolParts() replaces the entire parts array. If you remove built-in parts (text fields, direction arrow, engagement bar, etc.) they will no longer render on any symbol. Use this API only when you intend a full pipeline replacement.
ms.outline() — Generating Outlines from Draw Instructions
The ms.outline() utility takes a set of draw instructions and converts them to outlined (stroke-only) versions. This is used internally by the text field and engagement bar parts to produce the halo effect around labels.
ms.outline( drawInstruction, // drawInstruction or Array<drawInstruction> outline, // outline width (Number) stroke, // original stroke width (Number) color // outline color (String))// Returns: Array or Object of draw instructions
Example usage inside a custom symbol part:
ms.addSymbolPart(function myPart(ms) { const drawArray1 = []; const drawArray2 = []; const bbox = new ms.BBox(); const myShape = { type: "path", fill: this.colors.fillColor[this.metadata.affiliation], stroke: this.colors.frameColor[this.metadata.affiliation], strokewidth: this.style.strokeWidth, d: "M 80,80 L 120,80 L 120,120 L 80,120 Z" }; drawArray2.push(myShape); // Add an outline if the global outline is enabled if (this.style.outlineWidth > 0) { drawArray1.push( ms.outline( myShape, this.style.outlineWidth, this.style.strokeWidth, typeof this.style.outlineColor === "object" ? this.style.outlineColor[this.metadata.affiliation] : this.style.outlineColor ) ); } return { pre: drawArray1, post: drawArray2, bbox };});
All custom geometry is expressed as plain JSON draw instruction objects. The coordinate origin is at (100, 100) and the icon octagon is 100 px wide and 100 px tall. The available instruction types are:
{ type: "svg", svg: String // full SVG XML string to embed}
Multiple draw instructions can be grouped in an Array; the renderer handles both single objects and arrays uniformly.
The source file src/symbolfunctions/debug.js is the simplest self-contained example of a symbol part extension. Reading it alongside this guide gives you a working template to copy from. All other symbol parts in src/symbolfunctions/ demonstrate progressively more advanced patterns (text fields, direction arrows, engagement bars, HQ staves, etc.).
The docs/ directory in the milsymbol repository contains HTML test documents that render every symbol in the library. Opening these locally after adding a custom extension lets you verify that your new SIDC or label override looks correct across all affiliations and conditions.