How to write your own extension

Published May 21, 2026 · 6 min read


Writing your own extension is a powerful way to add custom geometric functions to the Geomatic editor. This guide walks you through the process step by step.

Quick start

The fastest way to get started is to use the template repository. Fork it, and you'll have a working extension structure ready to customize.


What is an extension?

An extension is a JavaScript module that exports one or more geometric functions. Each function follows a standard interface and runs in a sandboxed Web Worker, keeping it isolated from the editor's internal state.

The basic structure

Every exported function in your extension must have:

{
  name: string,         // PascalCase display name
  keyword: string,      // kebab-case command name
  parameters: UserParam[],
  outputType: string,   // Primary output type: 'Point', 'Scalar', 'Line', etc.
  compute: (inputs) => Record<string, any>,
}

Parameters define what inputs your function accepts:

{
  argName: string,      // Key name in compute inputs
  type: string,         // 'Scalar', 'Point', 'Line', 'Circle', etc.
  defaultValue: string | number,
  variadic: boolean,    // If true, captures remaining args
}

Writing the compute function

The compute function receives inputs as plain objects and must return a blueprint object:

compute(inputs) {
  // inputs is Record<string, any> keyed by argName
  // return Record<string, any> blueprint
}

Reading inputs

Different node types arrive as different structures:

  • Scalar: A plain number
  • Point: { type: 'Point', x: number, y: number }
  • Line: { type: 'Line', p1: { type: 'Point', x, y }, p2: { type: 'Point', x, y } }

Returning outputs

Always return plain objects with plain JS numbers:

return {
  main: { type: 'Point', x: number, y: number }  // 'main' is required
};

You can also return auxiliary nodes by adding extra keys:

return {
  midpoint: { type: 'Point', x: mx, y: my },  // auxiliary
  main: { type: 'Line', p1, p2 },              // primary output
};

Example: Distance between two points

export const Distance = {
  name: 'Distance',
  keyword: 'distance',
  outputType: 'Scalar',
  parameters: [
    { argName: 'p1', type: 'Point', defaultValue: 0, variadic: false },
    { argName: 'p2', type: 'Point', defaultValue: 0, variadic: false },
  ],
  compute({ p1, p2 }) {
    const dx = p2.x - p1.x;
    const dy = p2.y - p1.y;
    return { main: { type: 'Scalar', value: Math.sqrt(dx * dx + dy * dy) } };
  }
};

Example: Midpoint with auxiliary line

export const MidpointWithLine = {
  name: 'MidpointWithLine',
  keyword: 'midpoint-with-line',
  outputType: 'Point',
  parameters: [
    { argName: 'p1', type: 'Point', defaultValue: 0, variadic: false },
    { argName: 'p2', type: 'Point', defaultValue: 0, variadic: false },
  ],
  compute({ p1, p2 }) {
    const mx = (p1.x + p2.x) / 2;
    const my = (p1.y + p2.y) / 2;
    const midpoint = { type: 'Point', x: mx, y: my };
    const line = { type: 'Line', p1, p2 };
    
    return {
      midpoint_line: line,  // auxiliary
      main: midpoint,        // primary output
    };
  }
};

Module format

Save your functions in a .mjs or .js file using ES module syntax:

// my-extension.mjs
export const MyFunction = { /* ... */ };
export const AnotherFunction = { /* ... */ };

Recommended: Use a manifest.json to organize multiple files:

{
  "name": "My Geometry Pack",
  "version": "1.0.0",
  "description": "Custom geometric functions",
  "author": "Your Name",
  "homepage": "https://your-name.github.io/geo-pack",
  "extensions": [
    {
      "id": "my-function",
      "name": "MyFunction",
      "keyword": "my-function",
      "entry": "my-function.mjs",
      "parameters": [
        { "name": "p1", "type": "Point", "default": 0 },
        { "name": "p2", "type": "Point", "default": 0 }
      ],
      "outputType": "Point"
    }
  ]
}

Hosting your extension

Extensions must be hosted on GitHub Pages (*.github.io) with HTTPS. The recommended structure:

your-name.github.io/geo-pack/
  manifest.json       ← load this URL
  my-function.mjs
  another-function.mjs

Enable GitHub Pages in your repository settings (source: main branch, root or docs directory).

Important constraints

  • No network requests: fetch, XMLHttpRequest, WebSocket, EventSource, and importScripts are blocked in the worker
  • No DOM access: Extensions run in isolation
  • Plain objects only: All values must be plain JS numbers — no class instances or internal types
  • Degenerate cases: Return { main: { type: 'Dummy' } } for undefined results (e.g., parallel lines)

Loading your extension

Once hosted, load your extension by visiting the extensions page and pasting the URL to your manifest.json or .mjs file.

Loaded extensions are persisted to localStorage and automatically reloaded on future sessions.

Next steps

Ready to build? Check out the template repository to get started quickly, or read the full implementation guide for detailed documentation.