Skip to content

Scripting Basics ​

LoomCAD scripts are JavaScript code that automate design tasks. You can write scripts yourself or let the AI generate them from natural language descriptions.


How Scripts Work ​

Scripts use the Script.* interface to execute drawing and editing operations:

javascript
// Create a component named "ECU1" at position (200, 300)
const pins = [];
for (let i = 1; i <= 12; i++) {
  pins.push({ name: String(i), partnumber: "" });
}

Script.drawComponent({
  position: { x: 200, y: 300 },
  pinNumberingOrder: "column",
  flipped: false,
  compElementData: { name: "ECU1", partnumber: "ECU-001", pinCount: 12 },
  pinData: { x: 1, y: 12, pins },
});

Each command:

  • Takes a command name (e.g., 'drawComponent')
  • Takes an options object with parameters
  • Query commands return data directly: { success, elements: [...] }
  • All other commands (draw, update, erase, move) are asynchronous and do not return usable data to your script
  • All changes are applied after the script finishes executing

Execution Model

Draw, update, erase, and move commands are asynchronous — they are queued and executed in order. You do not need to check their return values. Errors are automatically collected and reported with line numbers after the script completes.

Query commands are the exception — they execute immediately and return data you can use in your script.


Running Scripts ​

From AI Chat ​

When the AI generates code, click Run script:

Code Execution Buttons

Results appear below the code:

Success: Execution Success

Error: Execution Error

Saved Scripts ​

Run saved scripts from the Scripts tab in the side menu:

Scripts tab Scripts tab with saved scripts

  1. Open the Scripts tab
  2. Select a script source from the dropdown (project scripts or library)
  3. Click the ▶ (play) button next to any script to run it

Each script in the list shows:

  • ▶ — Run the script
  • 📋 — Copy script to clipboard
  • ℹ — View script info
  • 🗑 — Delete script

Automatic Revision Checkpoints

Every time you run a script (from the Scripts tab or AI chat), LoomCAD automatically creates a named revision checkpoint before execution. Named revisions appear bold in the undo/redo history menu, so you can easily find and revert a script's changes in one step.


Script Output ​

Use scriptConsole (not console) for output:

javascript
// Available methods
scriptConsole.log("Information message");
scriptConsole.warning("Warning message");
scriptConsole.critical("Error message");
scriptConsole.table(data); // Display tabular data

Important

Regular console.log() won't display in the script results. Always use scriptConsole.


Command Categories ​

Draw Commands ​

Create new elements:

javascript
// Create a component
const pins = [];
for (let i = 1; i <= 8; i++) {
  pins.push({ name: String(i), partnumber: "" });
}

Script.drawComponent({
  position: { x: 100, y: 200 },
  pinNumberingOrder: "column",
  flipped: false,
  compElementData: { name: "J101", partnumber: "MOLEX-12345", pinCount: 8 },
  pinData: { x: 1, y: 8, pins },
});

// Create a wire between two pins
Script.drawWire({
  start: { componentName: "ECU1", pinName: "1" },
  end: { componentName: "J101", pinName: "1" },
  name: "PWR_WIRE",
  partnumber: "WIRE-18AWG-RED",
  colors: ["red"],
  coreSizeAWG: "18",
});

// Create a bundle segment
Script.drawBundle({
  start: { x: 100, y: 200 },
  end: { x: 300, y: 200 },
  name: "MAIN_TRUNK",
  partnumber: "BUNDLE-001",
});

Query Commands ​

Find and inspect elements. Queries use exact name matching (no wildcards):

javascript
// Find a component by exact name
const result = Script.queryComponent({ query: "ECU1" });
if (result.success && result.elements.length > 0) {
  const ecu = result.elements[0];
  scriptConsole.log(`ECU has ${ecu.pinCount} pins`);
}

// Get all wires, then filter in JavaScript
const allWires = Script.queryWire({});
const redWires = allWires.elements.filter(
  w => w.colors && w.colors.includes("red")
);
scriptConsole.log(`Found ${redWires.length} red wires`);

// Get all components, then filter by name pattern in JavaScript
const allComponents = Script.queryComponent({});
const jConnectors = allComponents.elements.filter(c => c.name.startsWith("J"));
scriptConsole.log(`Found ${jConnectors.length} J-series connectors`);

No Wildcards

Queries use exact matching only. You cannot use patterns like J* or PWR*. Instead, query all elements and filter with JavaScript.

Update Commands ​

Modify existing elements. Requires exact name or ID:

javascript
// Update a single component by name
Script.updateComponent({
  query: "ECU1",
  update: { partnumber: "NEW-PART-123" },
});

// Update a wire by name
Script.updateWire({
  query: "POWER_WIRE",
  update: { colors: ["red"], coreSizeAWG: "14" },
});

// To update multiple elements, query first then iterate
const allWires = Script.queryWire({});
allWires.elements.forEach(wire => {
  if (wire.name.startsWith("PWR")) {
    Script.updateWire({
      query: wire.name,
      update: { coreSizeAWG: "14" },
    });
  }
});

Move Commands ​

Reposition elements:

javascript
// Move to absolute position
Script.move({
  queries: [{ type: "component", query: "J101" }],
  position: { x: 500, y: 300 },
});

// Move relatively
Script.move({
  queries: [{ type: "component", query: "J101" }],
  position: { dx: 100, dy: -50 },
});

Erase Commands ​

Delete elements:

javascript
// Delete a component by name
Script.eraseComponent({ query: "J101" });

// Delete a wire by name
Script.eraseWire({ query: "W001" });

Caution

Erase commands are immediate. Use Ctrl+Z to undo if needed.


Error Handling ​

Query commands — check results directly ​

Query commands return data synchronously. Check the success property:

javascript
const result = Script.queryComponent({ query: "ECU1" });

if (!result.success) {
  scriptConsole.critical("Query failed: " + result.error);
} else if (result.elements.length === 0) {
  scriptConsole.warning("Component not found");
} else {
  scriptConsole.log("Found component: " + result.elements[0].name);
}

All other commands — errors are automatic ​

Draw, update, erase, and move commands are asynchronous. You do not need to check their return values. The script engine automatically collects all errors and reports them with line numbers after the script completes.


Complete Example ​

Here's a full script that creates a simple harness:

javascript
// Create a simple power distribution harness
// Parameters (modify these)
const FUSE_BOX_POS = { x: 200, y: 200 };
const LOAD_COUNT = 4;
const LOAD_SPACING = 150;

// Helper to create pin array
function makePins(count) {
  const pins = [];
  for (let i = 1; i <= count; i++) {
    pins.push({ name: String(i), partnumber: "" });
  }
  return pins;
}

// Create the fuse box
const fuseBoxPinCount = LOAD_COUNT + 1; // +1 for power input
Script.drawComponent({
  position: FUSE_BOX_POS,
  pinNumberingOrder: "column",
  flipped: false,
  compElementData: {
    name: "FUSE_BOX",
    partnumber: "FUSE-4WAY",
    pinCount: fuseBoxPinCount,
  },
  pinData: { x: 1, y: fuseBoxPinCount, pins: makePins(fuseBoxPinCount) },
});

// Create load connectors
for (let i = 1; i <= LOAD_COUNT; i++) {
  Script.drawComponent({
    position: {
      x: FUSE_BOX_POS.x + 400,
      y: FUSE_BOX_POS.y - 150 + i * LOAD_SPACING,
    },
    pinNumberingOrder: "column",
    flipped: false,
    compElementData: {
      name: "LOAD" + i,
      partnumber: "LOAD-CONN",
      pinCount: 2,
    },
    pinData: { x: 1, y: 2, pins: makePins(2) },
  });
}

// Create wires from fuse box to loads
for (let i = 1; i <= LOAD_COUNT; i++) {
  // Power wire
  Script.drawWire({
    start: { componentName: "FUSE_BOX", pinName: String(i) },
    end: { componentName: "LOAD" + i, pinName: "1" },
    name: "PWR_LOAD" + i,
    partnumber: "WIRE-16AWG-RED",
    colors: ["red"],
    coreSizeAWG: "16",
  });

  // Ground wire (from load pin 2, no destination - open end)
  Script.drawWire({
    start: { componentName: "LOAD" + i, pinName: "2" },
    end: { componentName: "LOAD" + i, pinName: "2" }, // Same pin for stub
    name: "GND_LOAD" + i,
    partnumber: "WIRE-16AWG-BLK",
    colors: ["black"],
    coreSizeAWG: "16",
  });
}

scriptConsole.log("Created power distribution with " + LOAD_COUNT + " loads");
// Note: All changes are applied after the script finishes

Best Practices ​

Use Variables for Repeated Values ​

javascript
// Good: Easy to modify
const WIRE_GAUGE = "18";
const SIGNAL_COLOR = "white";

Script.drawWire({
  name: "SIG1",
  coreSizeAWG: WIRE_GAUGE,
  colors: [SIGNAL_COLOR],
  fromComponent: "ECU",
  fromPin: 1,
  toComponent: "SENSOR",
  toPin: 1,
});

Comment Your Intent ​

javascript
// Create connector J1 - main power input from vehicle battery
Script.drawComponent({
  name: "J1",
  // Position at left edge of sheet
  x: 50,
  y: 200,
  pinCount: 4,
});

Query Before Bulk Operations ​

javascript
// First, query and filter to see what will be affected
const allWires = Script.queryWire({});
const targetWires = allWires.elements.filter(w => w.coreSizeAWG === "22");
scriptConsole.log("Will update " + targetWires.length + " wires");

// Then update each one
targetWires.forEach(wire => {
  Script.updateWire({
    query: wire.name,
    update: { coreSizeAWG: "20" },
  });
});

Use Exact Names ​

javascript
// ✅ CORRECT: Use exact component name
Script.queryComponent({ query: "POWER_CONNECTOR" });

// ❌ WRONG: Wildcards don't work
Script.queryComponent({ query: "POWER*" });

// ✅ CORRECT: Filter in JavaScript instead
const all = Script.queryComponent({});
const power = all.elements.filter(c => c.name.startsWith("POWER"));

Next Steps ​