play_arrow

Movement Execution

The primary control for executing robot motion. Use single click for incremental steps or the submenu for full automation. Double click on the play will perform the full automation as well.

fast_forward Play full
Executes all movements in the current array, ending with the move from the last posture back to the first (unless Close the loop is switched off in Settings).
arrow_back_2 Play back
Steps backward through your position history.
Playback Demo
list_alt

Posture Queue [0/0]

Manage the sequence of movements for the robot arm. The counter displays your active position versus total positions.

add_circle Add pose
Inserts position relative to current index.
first_page Add as first position
Inserts position at the start of the array; the others move up by one.
add_box Add as last position
Appends position to the end of the array.
cancel Remove pose
Deletes the current posture from the queue.
sync_saved_locally Update posture
Overwrites the current posture with the arm's pose as it stands in the workspace (joints, and rail / table with external axes); the movement settings stay.
smart_toy Posture info
Movement type, dwell, velocity and plugin action of the current posture (see Interactive Controls → Movement Types).
Posture Queue Demo
carry_on_bag

Pick and Place Logic

Operational Flow

To interact with objects in the scene, follow this logic strictly:

  • 1. Create a standard position as a "safe start".
  • 2. Move arm to the object using Gizmos or IK.
  • 3. Use Add Position, then select your interaction:
carry_on_bag_checked Pick object
carry_on_bag_inactive Drop object
restore Restore object
Returns object to original imported position.
mouse

Interactive Controls

Mouse And Keyboard

Direct Kinematics: Click and drag joints with the mouse.

Inverse Kinematics (IK): Hold SHIFT while dragging to solve for position.

Object Movement: Click any .stl to reveal the 3D Gizmo for relocation.

Object size from a plugin: getWorkspaceObjectBoundingBox(objectIndex) returns the object's axis-aligned box in world coordinates, { min, max, size, center } in mm with its rotation and position applied, for example to place the tool above an object: center[2] + size[2] / 2 is its top. It returns null while a robot is carrying the object.

Zoom: Mouse wheel, or a two-finger pinch on a touch screen: spreading the fingers zooms in, bringing them together zooms out, in proportion to the finger movement.

Linear rail: Drag the rail carriage to slide the arm along the rail. Hold SHIFT while dragging and only the base moves: the manipulator keeps its exact world position (X, Y, Z) and orientation (Rx, Ry, Rz), the joints are re-solved for every step. Where the pose cannot be reached from the new base position inside the joint limits, the carriage stops instead of letting the tool drift.

View: Drag the background to orbit, use the wheel to zoom. Hold SHIFT and drag the background to move the view center in X / Y, hold ALT (Option on Mac) and drag for its height (Z); the scene follows the mouse. Adding a second arm centers the view between the arms, Center the view in Settings brings the center and the zoom back to their defaults, and the view center is saved with the workspace.

Joint Conventions

The simulator counts every joint positive counter-clockwise about its axis vector, with zero at the model's home pose. Real controllers differ in sign and zero, so each .rob row carries a mapping real = sign · sim + offset (columns 14 / 15: sign, offset in degrees). Every joint angle that crosses the plugin API - getCurrentJointAngles(), getHomeJointAngles() (the simulator's Home pose, all joints at 0 in the simulator's convention, as API angles), worldToJointAngles*(), jointAnglesToWorld*(), postures, trajectory deltas, limits - is in the real convention; getJointAnglesMapping() / setJointAnglesMapping() read and change the mapping at run time (e.g. [{sign: 1, offset: 0}, {sign: -1, offset: -Math.PI/2}, {sign: -1, offset: Math.PI/2}, …] for a KUKA whose home is the candle pose).

Joint Limits

Each robot's .rob file carries the manufacturer's joint angle range (columns 12 / 13, degrees, real convention) of every joint; the simulator derives its own limits through the mapping. Dragging a segment stops at its limit, the joint sliders span the limits, and IK solutions outside them are rejected (the pivot gizmo stops, Apply XYZ finds another solution or none). Plugins can read the limits with getRobotJointLimits(), check a posture with isPostureValid(), and simulateRobotMotion() refuses a trajectory that would leave them. Models whose limits are not the manufacturer's values report a warning in the console when loaded.

Singularities: a plugin can check a Cartesian path before it is simulated with findTrajSingularity(path, q0, normal, robotIdx, K = 10). The path is solved point by point like worldToJointAnglesBatch(), and the conditioning of the arm's Jacobian at each solution is compared with the best point on the path. It returns null when the path is fine, otherwise [[index, K], …]: the path index and how many times worse that point is conditioned (0 = exactly singular, the arm loses a degree of freedom there, joints would have to spin). Points near the flagged indices should be moved, or the tool normal changed, before simulateRobotMotion(). The demo plugin shows the call.

Trajectory line: the drawn path is an anti-aliased line of constant pixel width (it does not grow with the zoom). Its width and colour are set in Settings → the palette button beside Show path (Path style window), which also holds the singularity marking: the heaviest line weight and the start / end colour of the gradient plugins use; defaults are 1 px (the grid line weight), teal, 3 px, light red to full red. A segment always has one width and colour, that of its styled end point; plugins read them with getTrajLineStyle(), change them with setTrajLineStyle(width, color), and can give single steps of the current trajectory their own width and colour with setTrajStepStyle([[stepIndex, width, [r, g, b]], …], robotIdx) ([] clears). The demo plugin colours the steps that findTrajSingularity() reports with the gradient and the weight from the Path style window (defaults: light red to full red, grid weight to 3 px), the worse the conditioning the redder and heavier the line. getTrajLineStyle() returns them as singularWidth, singularColorStart and singularColorEnd, and setTrajLineStyle(width, color, { width, colorStart, colorEnd }) changes them. A new trajectory starts with the defaults again.

Movement Types

Each posture in the queue carries how the arm travels from it to the next posture (Posture Queue → Posture Info): a Line set on posture 1 is the move between postures 1 and 2, and the last posture's type closes the loop back to the first. Free Move interpolates the joint angles with a trapezoidal velocity profile; Line, Arc (radius, plane normal, short / long, CW / CCW; Full circle draws a whole circle of the radius from the start position back to it when both postures coincide, CW or CCW, with the centre in the direction of the tool normal at the start turned by the starting angle about the plane normal) and 3D Curve (a spline through user mid points) move the tool centre point exactly along that path in world space. The orientation changes smoothly on the way: the three wrist joints are interpolated between the two postures with the same trapezoidal velocity profile, the other joints are solved for every path point, and the move ends exactly on the saved posture (position and orientation). The arc's plane normal and the mid point fields of a 3D curve follow the manipulator while their lock chip is closed, like the read-out in Joint Control: the normal takes the manipulator's normal when Arc is chosen and whenever you move the arm afterwards (a playing simulation does not change it), and the curve point shows the tool position, so you teach a mid point by moving the arm and pressing Add. Open the lock to type values by hand; selecting a stored point in the list opens it for editing.

The same window sets per posture a dwell time (seconds the arm pauses at the posture before the move that starts there; dwells apply to Play Full Sequence only, which opens with the dwell of its first posture, not to single steps), a velocity 0..1 for the move that starts at it (or "Use default" = the global velocity from Settings) and Plugin Action Execute: the action belongs to that posture. When the running simulation reaches a flagged posture it stops there, the plugin's async function userAction(robotIdx, postureIdx, velocity, dwellTime, gcodeString) is called, and the simulation continues (with the posture's dwell, then the next move) only after it has returned. A run that starts at a flagged posture calls its action first.

gcodeString is the text of the posture's G-Code or Custom Action box (enabled only with Plugin Action Execute), so a plugin can forward G-code lines or any custom command to a controller. For Line, Arc and 3D Curve the window offers two more settings: Align normal with the path makes the manipulator's normal follow the curve (at each point the curve normal closest to the one carried from the start pose is taken, and the first and last quarter of the path blend from the start pose's orientation into the aligned one and back into the end posture's), solved with the full-orientation IK; and Detect singularities with its threshold K (default 12): like every other setting it belongs to the move that starts at the posture. With the box checked on posture 1, the Cartesian move from posture 1 to posture 2 is checked like findTrajSingularity(), its singular steps are drawn with the Path style marking (they are on the line when the arm has arrived and posture 2 is the active one), and a console warning names them; unchecked (the default) the check is skipped and the line stays plain. In moveParams / getPostureMoveType() these are dwell, velocity (null = default), pluginAction, gcodeString, alignNormal, detectSingularities and singularityThreshold. Everything is saved in the .brw workspace file.

Milling Program is the fifth type: the move that starts at the posture runs a Mill / Sculpt program (see the Mill / Sculpt section). The arm first travels with a free move (trapezoidal velocity profile, rail and table included) from the posture to the program's first state, then the program is played (arm, rail and table together, carving the workpiece), and from its last state a free move takes the arm on to the next posture. Postures before and after it are ordinary ones, so a sequence can be "home → milling → inspection pose → home". The panel shows which program the posture uses: choosing the type attaches the program generated in the Mill / Sculpt window; Use generated program re-attaches it after a new generation and Open Mill / Sculpt opens the window. Play back turns a milling move into a free move. The program itself is not written into the .brw file (it is far too large); after loading a workspace, generate it again or set it from a plugin, otherwise the posture falls back to a free move with a console warning. The program's steps draw no path line.

From a plugin: In the plugin API the type is the integer enum brwAPI.MoveType = { FREE: 1, LINE: 2, ARC: 3, CURVE: 4, MILL: 5 }: addRobotPosture(jointAngles, index, robotIdx, moveType = 1, moveParams) takes it as an optional fourth argument (default Free Move) with moveParams = { radius, normal: [x, y, z], long, cw, full, startAngle } for an arc, { points: [[x, y, z], …] } (world, mm) for a curve or { program } for a milling program, where program is { name, states: [{ jointAngles, rail, table, cut }, …] } (joint angles in the real convention, rail travel in mm, table angle in degrees counting turns, cut = { layer, cell0, cell1, radius } on the blank's grid or null) - the form getMillingProgram(index = -1, robotIdx) returns for the program generated in the Mill / Sculpt window (index -1) or attached to a posture, so a plugin can store, modify or build one; program: null (or no program) uses the generated one; updateRobotPosture(index, robotIdx) overwrites a posture (default: the current one) with the arm's pose right now, keeping its move settings; getPostureMoveType(index, robotIdx) returns { moveType, radius, normal, long, cw, points } and setPostureMoveType(index, moveType, moveParams, robotIdx) changes an existing posture.

3d_rotation

Kinematics & Coordinates

Joint Control

Set specific angles for each robot joint within its physical range. The system uses inverse kinematics calculations to position the arm. The sliders always show simulator angles with zero at the model's home pose (see Joint Conventions for the mapping to a real controller). M+ stores the current joint angles on a stack and M- recalls and removes the last stored set (last in, first out); the XYZ windows have the same pair for positions.

Kinematic configuration type: each model's .rob header carries a type (9th value): 1 for a standard six-axis articulated arm (the default) and 2 for a Universal Robots type arm (UR3, UR5, UR10e). The direct and inverse kinematics use the type of the arm they are solving for; plugins can read it with getRobotConfigType(robotIdx).

home Home
Resets the arm to its initial '0' state.

Joint Lock / Unlock

Each joint in the 3D control panel can be toggled between two modes:

lock Locked
Joint angle values update automatically but cannot be changed manually.
lock_open Unlocked
Joint angles can be edited manually but will not update automatically.

XYZ Coordinates

Work in world coordinates relative to robot reach. Click any imported .stl object to use the from object option via the appearing gizmo.

XYZ & Orientation is a second window that adds the tool orientation as Rx, Ry, Rz (degrees, read from the arm's forward kinematics while the lock chip is closed) and solves position and orientation together with the full-orientation IK on Apply. Both windows have a Key/Mouse Input button: it opens a small window to type x, y, z and the normal or the angles, with Click in Workspace taking the next left click in the viewport as the target point (the same button exists in the 3D curve's Edit points window). Settings → Display cursor coordinates shows the world point under the mouse in a box above the console.

token XYZ controls

Access the XYZ coordinate window here. Note: All workspace windows are draggable and can be positioned anywhere in your browser.

precision_manufacturing

Robot Library

Choose from a growing library of professional robot arms. Selecting a model will instantly replace the active arm in the scene while maintaining your coordinate space; with several arms loaded, the other arms are untouched (see Multiple Robot Arms).

  • • Yaskawa / MotoMini
  • • KUKA Models
  • • Universal Robots & more
Playback Demo
folder_zip

Import / Export Workspace

Demo projects: the first link in the bar at the top, Demo Projects, downloads ready-made projects as one zip file (a window explains the steps first). Unzip it, then load a project with Import Workspace by choosing the project's folder, and click Play, or a User Command button for a project that holds only plugin code, such as the singularity demo. The collision avoidance demo shows the robot hitting a yellow box when played; Tools & Objects → Collision Avoidance with Precision 0 and Recalculate trajectory makes it pass around the box.

Save and restore your entire workspace including the robot configuration and all imported objects. The workspace is stored as a .brw file next to the STL files it refers to: every scene object, every tool of the library and every turntable workpiece (imported or generated in Mill / Sculpt, in its milled state), so a project started from scratch is complete after one export.

download Export workspace
Saves your current session - arm positions, queue, external axes, tools and all STL objects. In Chrome and Edge a folder picker opens: choose where to save (Downloads, for example) and a sub folder named after the robot and the time is created with the .brw and the STL files. In other browsers, or when the picker is cancelled, a single .zip with the same content is downloaded instead; unzip it into a folder.
upload Import workspace
Choose the folder that holds the .brw file and its STL files to restore your full workspace and continue where you left off.
Import Export Demo
highlight

Object Highlighting

When manipulating objects in the scene, the active object is now visually highlighted so you can clearly see which item you are interacting with. This improves precision when working with multiple objects in close proximity.

highlight_alt Active highlight
The currently selected object is outlined to distinguish it from surrounding scene elements.
Highlighting Demo
view_in_ar

Scene & System

Object Management

attach_file_add Import 3D Object
Opens the object window: the workspace objects as chips (click one to select it, as in the viewport), the active object's position, rotation Rx / Ry / Rz and colour, Add Object to import STL files and Remove Object to delete the active one. Position and rotation are editable once the lock chip is unlocked and apply at once.
view_in_ar External Axis
Linear rail and rotary table of the active arm, see the External Axes section.
carpenter Mill / Sculpt
Robot milling of a blank on the rotary table into an imported STL object, see the Mill / Sculpt section.
safety_check Collision Avoidance
Finds collisions between the arm and the workspace objects along the posture queue and recalculates the moves around them, see the Collision Avoidance section.

Robot Library

Click precision_manufacturing to swap the active arm's model (KUKA, Yaskawa, etc.), add or remove arms and set their base positions.

Configuration

  • • Velocity: Control arm movement speed (default 0.35).
  • • Show Pivot/Path: Visualize the joint gizmos and the movement trajectory. The palette button beside Show path opens the Path style window (line weight, colour and the singularity marking, see Interactive Controls).
  • • Simulation repeat: how many times Play Full Sequence runs the whole queue in a row (1 to 1000, default 1).
  • • Close the loop: on (default), Play Full Sequence ends with the move from the last posture back to the first, with the last posture's movement type, and the first posture is the current one afterwards; off, a full run plays the queue from the first posture and stops at the last one, which is then the current one (the counter and getRobotCurrentPostureIdx() say so); when the arm is not at the first posture, for example after the previous run, a click on Play Full only brings it there with a free move, and the next click plays the queue. Remembered in the browser.
  • • Display cursor coordinates: shows the world coordinates of the point under the mouse in a box above the console. This and Show path are remembered in the browser and restored when the application starts.
  • • Show AI Assistant: shows or hides the AI Assistant panel at the right edge (see AI Assistant).
  • • Language: Multi-lang support (EN, DE, IT, FR, ES, ZH, JP).
  • • Dark Mode: Switch the interface between the light and dark themes.
precision_manufacturing

Multiple Robot Arms

A scene is no longer limited to one arm. Add as many robots as the cell needs, place each one at its own base position and program them side by side - from the interface or from a plugin. Below, two arms driven by one plugin, with the console reporting the run.

Two robot arms in one scene, driven by a plugin

Arms And Slots

Each arm is a slot with its own model, base position, posture queue, trajectory and pick & place state. Workspace objects are shared, so two arms can hand parts to each other.

Open precision_manufacturing Robot Model: the chips at the top of the window list the arms as 1: Model [x, y, z]. The outlined chip is the active arm - the one the joint / XYZ controls, the posture queue, pick & place and the plugin API act on. Click a chip, or click any part of an arm in the viewport, to make it active.

add Add Robot
Adds an arm with the active arm's model, offset along X so the two do not overlap. Pick another model in the gallery afterwards if needed.
remove Remove Robot
Removes the active arm. One arm always stays.
my_location Base position (mm) / rotation Rx Ry Rz (°)
World X / Y / Z of the active arm's base and its rotation about the X, Y and Z axes (composed as Rx · Ry · Rz, the same convention as the STL objects; Rz alone turns a floor-mounted arm, Rx / Ry tilt it for wall or ceiling mounting). Apply (or Enter) moves and turns the arm together with its gizmo, a carried object and its drawn path. Position, angles and the resulting 3×3 rotation matrix are saved with the workspace (.brw) and restored on import.

Working With Several Arms

  • 1. Pick a model in the gallery: it replaces the model of the active arm only.
  • 2. Queue postures per arm as usual; the [current/total] counter follows the active arm.
  • 3. Play Full Sequence runs every arm that has postures at the same time. Play Once and Play Back step the active arm only.
  • 4. Pick & place is per arm: select the object, then pick / drop on the active arm's postures. An object carried by one arm is drawn with that arm.
  • 5. XYZ coordinates, tool normals and every world position in the API are absolute scene coordinates, i.e. they include the arm's base position and base rotation; forward and inverse kinematics convert to and from the arm's own frame.

Workspaces (.brw, version 2.1) save all arms with their base positions, postures and pick & place. Older single-arm files still load as one arm at the origin.

From A Plugin

Robot-related API calls take an optional trailing robotIdx. Arms play concurrently, so start each one and they move together:

// second arm 1.5 m along X, same model as the active one
const b = await brwAPI.addRobotArm(-1, [1500, 0, 0]);

for (const k of [0, b])
{
  const q0    = await brwAPI.getCurrentJointAngles(k);
  const start = await brwAPI.getCurrentWorldPosition(k); // base included
  const path  = await brwAPI.worldToJointAnglesBatch(wpos(start), q0, null, k);
  await brwAPI.simulateRobotMotion(steps(path), k);       // returns at once
}
handyman

Tools

Attaching A Tool

Open view_in_ar Tools & Objects → Tools. Import tool .stl reads one or several STL files into the tool list (named after the file). Select a tool in the list and press Attach Tool: it replaces the arm's default end effector (the 7th part of the model) and the arm's tool point moves to the tool offset - dx / dy / dz from the flange, which take the place of the 7th row of the robot's .rob file. Kinematics, the XYZ read-out, the drawn path, the pivot gizmo and pick & place all work at the tool point. Detach Tool puts the original end effector and offset back.

Model a tool with its attach point at the origin and pointing along +X; on attach it is moved to the flange (the sum of the model's joint translations, rows 1 … 6). With no tool attached, the offset fields show the .rob values read-only; with a tool they and the colour picker edit the attached tool at once.

Saved With The Workspace

The tool list (names and STL file names) and each arm's attached tool with its offset and colour are written to the .brw; on import the STLs are read from the workspace folder by name, like the other objects, and re-attached. Keep the tool STL files next to the .brw.

From A Plugin

getToolNames() lists the imported tools, attachRobotTool(name, offset, color, robotIdx) mounts one (replacing a mounted tool), detachRobotTool() removes it, setRobotToolOffset() / setRobotToolColor() edit it and getRobotToolInfo() reads it back. A plugin cannot read files, so tools are imported in the Tools window or come with the workspace.

const tools = await brwAPI.getToolNames();          // e.g. ['gripper', 'welder']
await brwAPI.attachRobotTool('welder', [150, 0, 0]); // tool point 150 mm from the flange
// ... run the path ...
await brwAPI.detachRobotTool();
conveyor_belt

External Axes

Linear Rail

Open view_in_ar Tools & Objects → External Axis. The window works on the active arm and has two panels. The top one is a linear rail: base position (bottom of the rail, at its start, centred on the profile), the translation vector the carriage moves along, and the profile width / height plus the length. Add linear axis generates the rail base and its carriage and puts the arm on the carriage - the arm's base becomes the rail start, one rail height up.

Drag the carriage in the viewport to move it (the arm rides along); it stops at the rail ends (0 … L). Hold SHIFT while dragging to keep the manipulator where it is: the base slides along the rail, but the tool keeps its world position and orientation (the joints are re-solved each step, and the carriage stops where the pose would leave the joint limits or the reach). Each arm can have one rail; Remove axis takes it away and leaves the arm where it stands.

Rotary Table

The bottom panel adds a rotary table (external positioner): base position, rotation Rx / Ry / Rz of the table, radius and height. Import workpiece STL puts a part on the turntable, one table height above the base, centred on the rotation axis; the Workpiece row names the STL that sits on the table (or the Mill / Sculpt blank), and the colour picker sets its colour (saved with the workspace). Drag the turntable or the workpiece to turn them together; the table does not move the arm.

Once an axis exists its fields are read-only. The lock chip next to the arm name (as in Joint Control) unlocks them: every change is applied at once - the rail or table is rebuilt, the carriage keeps its travel and the arm stays on it.

Postures And Simulation

When an arm owns an external axis, a saved posture carries the rail travel, the table angle and the arm's base position along with the six joint angles. Playing the queue then moves the rail, the whole arm and the turntable together with the joints: every axis follows a trapezoidal velocity profile over the same number of steps, so they start and stop together, and the velocity setting applies to all of them. The drawn path follows the arm along the rail.

Rails and tables are saved in the workspace (.brw) and rebuilt on import; the workpiece STL is loaded from the workspace folder by name, like the other objects.

From A Plugin

addLinearRail() / addRotaryTable() create the axes (a rail has a base position and a rotation rot: [rx, ry, rz], composed Rx·Ry·Rz like the robot base; its travel direction dir is the rotated +X and is returned by getLinearRail(); the robot is mounted on the carriage, so moving or rotating the robot base takes the rail along and rotating the rail turns the robot), setRailPosition() (mm) and setRotaryTableAngle() (degrees) move them, and simulateRobotMotion(jointSteps, robotIdx, railSteps, tableSteps) plays joints, rail and table together - one delta per step, rail in mm, table in degrees, shorter arrays padded with zeros. Postures from getRobotPostureArray() carry [j0..j5, travel, angle (rad), bx, by, bz] and trajectory steps from getRobotTrajData() carry the rail / table deltas as elements 6 and 7 when an axis exists; the workpiece STL can only be attached in the interface.

// rot = [rx, ry, rz] in degrees, or dir: [tx, ty, tz] instead;
// with neither the rail is aligned with the robot base
await brwAPI.addLinearRail({
  basePos: [0, 0, 0], rot: [0, 0, 0],
  W: 500, H: 300, L: 3000
});

const q0    = await brwAPI.getCurrentJointAngles();
const steps = 100;
const rail  = Array(steps).fill(2000 / steps);   // 2 m along the rail
const table = Array(steps).fill(90 / steps);     // a quarter turn

await brwAPI.simulateRobotMotion([], undefined, rail, table); // joints stay
carpenter

Mill / Sculpt

Setup

Robot milling of a blank on the rotary table. The active arm needs a linear rail and a rotary table (Tools & Objects → External Axis). Open carpenter Mill / Sculpt: Import target STL loads the object to be milled (it is centred on the table axis and set on the table top), Calc bounding box fills the box size W, D, H with the object's bounding box (whole millimetres, rounded up; edit the values to leave more material), and Create blank box puts a box of that size on the table as the workpiece. The target must fit inside the blank; the order does not matter, the target's profile is computed whenever both exist. Layer height and angular step are the resolution of the workpiece model; the tool diameter, the depth of cut per pass and the distance the rail keeps between the robot base and the tool line are the milling parameters. Tool approach angle tilts the tool from the horizontal (default 45°): it points at the table axis from above, so the tool body stays outside the material below the tip. Max table speed (°/s, at speed 1) limits the rotary table: near the axis one cell would otherwise pass under the tool in a single step. Creating the blank and importing the target show the progress popup as well.

How The Program Is Made

The workpiece is described as a radius for every angle and height around the table axis; the target object is converted to the same form by rays from the axis, so shapes with undercuts along the radius are milled to their radial silhouette. The tool points at the axis from one direction, tilted down by the approach angle, and the table turns the material under it: one turn mills one layer. Layers go top-down in passes of at most the depth of cut; between two cells the tool moves radially first when the radius grows and turns first when it shrinks, so every cell ends exactly at its target radius. For each layer a few rail positions are tried and the one with the best worst-case Jacobian conditioning inside the joint limits is used; the rail moves with the arm frozen, and the arm then travels to the new clearance pose with a joint move (trapezoidal profile). Steps are spaced by a constant travelled distance derived from the simulation velocity, so the surface speed stays near constant. When the program is ready, the active posture of the arm gets the Milling Program movement type with it (a queue without postures first gets the arm's current pose as posture 1).

Simulation

Generate milling program shows a progress popup that advances one layer at a time and has a Cancel button. Start simulation remembers the arm, rail and table state of the first run (the pre-milling state); every run first returns there with a free move (trapezoidal velocity profile, the table by its shortest turn) - a run started again after Stop comes back from wherever it was interrupted - then moves to the program's first state and plays the program like Play Full Sequence, moving arm, rail and table together; the workpiece mesh is carved live, step by step, so at the end it is the target object. The generated program can also be the move of a posture in the queue: choose Milling Program as its movement type in Posture Info (see Interactive Controls → Movement Types). Layers are milled top-down, so at first only the top of the blank changes. Speed is the number of planned steps played per frame (1 keeps the planned surface speed; 5 or more makes the carving visible at a glance). The pause button pauses and resumes, stop ends the run, and the two arrows jump 10 % of the program backwards or forwards: the arm, rail and table are put into the state of that step and the workpiece is rebuilt from the blank up to it. The status line shows the step, the percentage and the share of the blank already removed. Programs are long (a 200 mm box to a 100 mm object is about a hundred thousand steps), so the path line is not drawn for them.

Limits

The tool cuts one layer per pass with a footprint half a diameter to each side, so features narrower than the tool are rounded; undercuts along the radius cannot be produced. The blank exists only in memory: the name it shows (blank_W x D x H.stl) is the file name the workspace refers to. A workspace export writes the untouched blank under that name (whatever has been milled since) and the target STL next to it, and the table config keeps the blank's parameters; importing that folder restores the Mill / Sculpt window with the blank, the box fields and the target, so only the program has to be generated again (it is not saved, see Movement Types). Export workpiece STL downloads the workpiece as it is now: the blank under its name, or a milled state as milled_<name>. The rotary table's angle keeps counting turns during a program.

safety_check

Collision Avoidance

What It Does

Tools & Objects → Collision Avoidance checks the moves of the active robot's posture queue against the objects in the workspace and, when a move comes too close to one, recalculates it so that the arm passes around the object. The check runs on the same trajectory steps Play uses, so Line, Arc and 3D Curve moves are tested along their real path.

Settings

  • • Avoid all objects in workspace (default) or Select objects to avoid: with the box unchecked, the button lists the objects with a checkbox each. Objects the arm picks up are left out automatically.
  • • Avoid floor plane with its Z-offset (mm): the floor becomes an obstacle too, as the plane z = offset, which can be set above or below z = 0. The arm's first link, the column standing on the floor, is not tested against it. With the floor checked, a check is possible even without any object.
  • • Detect collisions only: nothing is changed; colliding moves are reported in the window and the console.
  • • Threshold distance (mm): the clearance the arm must keep. A move is a collision when the arm comes closer than this.
  • • Precision factor: at the minimum each object is its bounding box, which is fast and right for simple convex objects. Higher values add points sampled on the object's surface, up to several thousand at the maximum, so the arm can pass through the free space of a concave object, such as under a table top, at the cost of calculation time.
  • • Choose recalculate method: Insert New Postures puts new postures with free moves into the queue between the two postures of a colliding move; Insert Curve Points keeps the two postures and turns the move into a 3D Curve whose mid points lead the tool around the object.

Recalculate trajectory runs the check and the recalculation (a progress window shows the move being worked on and can cancel it). Back to original trajectory restores the postures and movement types as they were before the first recalculation. After a check, the colliding steps of every played trajectory are drawn red on the path.

How It Works

The arm is modelled with capsules fitted to the mesh of every moving link: the link is cut into slices along its main direction and each slice gets a capsule of its own radius, so a link that is thick at the joint and slim along the arm is followed closely (an attached tool included). An object is its oriented bounding box, refined by the surface points when the precision factor is above the minimum. For every tested pose the distance from each capsule to each object is compared with the threshold.

A colliding move is replanned in joint space with a bidirectional rapidly-exploring random tree (RRT-Connect): two search trees grow from the two postures towards each other, only through configurations that are inside the joint limits and clear of the objects. The found path is then shortened to a few via configurations, which become the new postures or the curve points. The result is accepted only when the real trajectory steps pass the check; otherwise the search is repeated with a larger reserve.

From A Plugin

setCollisionAvoidanceParams(params) sets the settings like the window's controls, only the given ones: { avoidAll, objects, detectOnly, threshold, precision, method: 'curve' | 'postures', avoidFloor, floorZ }, and returns them as they are afterwards. setCollisionAvoidanceObjects([indices]) chooses the objects to avoid when not all are. recalculateCollisionAvoidance(robotIdx) runs the check and the recalculation and resolves with { ok, collisions, recalculated, unsolved, messages, error }; restoreOriginalTrajectory(robotIdx) brings the original queue back.

await brwAPI.setCollisionAvoidanceParams({ threshold: 20, precision: 0, method: 'postures' });
await brwAPI.setCollisionAvoidanceObjects([0]);            // only object 0
const r = await brwAPI.recalculateCollisionAvoidance();
brwAPI.logMessage(`${r.collisions} colliding move(s), ${r.recalculated} recalculated`);
// await brwAPI.restoreOriginalTrajectory();               // undo

Limits

The check covers the arm against workspace objects and, when checked, the floor plane. It does not cover the arm against itself, other robot arms, the rail and table parts, or an object while it is carried. A posture of the queue may itself be closer to an object than the threshold, an approach pose beside a part for example: it is kept and reported, and the moves leave and reach it through a zone in which the required clearance grows from the posture's own clearance to the full threshold. Only a posture that touches an object cannot be used: move it or deselect the object. Insert Curve Points can fail where the tool path alone cannot keep the whole arm clear; Insert New Postures is the more reliable method. Inserted postures use free moves, so a Line or Arc move that collided becomes a sequence of free moves.

code_blocks

Plugin System

What It Is

Plugins let you drive the arm from your own JavaScript instead of clicking through the interface. Use them to generate toolpaths, batch postures, or run a whole routine unattended. Your code runs in a sandboxed Web Worker, so it has no access to the page; it reaches the application only through the injected brwAPI object.

Loading a Plugin

  • 1. Put brw-plugin.js in a folder, on its own or beside a .brw workspace.
  • 2. Open swap_vert Import / Export and select that folder.
  • 3. Run it with one of the three command buttons.
code_blocks User command 1 / 2 / 3
Calls userCommand(1), (2) or (3).

A workspace is optional. If you only want the plugin commands, a folder holding nothing but brw-plugin.js loads fine. Selecting a folder with neither file shows a message instead.

Entry Point

Your file must define userCommand. The argument tells you which button was pressed, so a single plugin can carry three separate routines:

async function userCommand(cmdId)
{
  brwAPI.logMessage('Plugin started');

  const q0    = await brwAPI.getCurrentJointAngles();
  const start = await brwAPI.getCurrentWorldPosition();
  const vel   = await brwAPI.getSimulationVelocity();

  if (cmdId == 1)
  {
    // ...build a path, then play it back:
    await brwAPI.simulateRobotMotion(simDataArr);
  }
}

Every brwAPI call is asynchronous. Each one is forwarded to the application and resolved back to the worker, so it must be awaited.

Install the BabaCAD Robotics VS Code extension for API auto-complete.

Running A Command

Pressing a command button runs your userCommand straight away, against the arm as it currently stands. Here command 1 traces a heart at the tool, with the console reporting each stage as it goes.

Nothing is queued into the posture list, so a plugin can be re-run as often as you like while you tune it.

Running user command 1 from a plugin

API Reference

Robot-related calls take an optional trailing robotIdx (the arm's slot index, 0-based); when omitted they act on the active arm. World positions and normals are absolute and include the arm's base position and rotation; addRobotArm(modelIdx, basePos, baseRot), getRobotBaseRot() and setRobotBaseRot() use [rx, ry, rz] in degrees. The ten *Robot* calls after getCurrentNormalVector() manage the arms themselves: count, active arm, model, add / remove and base position.

logMessage() worldToJointAngles() worldToJointAnglesBatch() findTrajSingularity() jointAnglesToWorld() jointAnglesToWorldBatch() getCurrentJointAngles() getCurrentWorldPosition() getCurrentNormalVector() getRobotCount() getActiveRobotIdx() setActiveRobotIdx() getRobotModelNames() getRobotModelIdx() getRobotConfigType() setRobotModelIdx() addRobotArm() removeRobotArm() getRobotBasePos() setRobotBasePos() getRobotBaseRot() setRobotBaseRot() getLinearRail() addLinearRail() removeLinearRail() getRailPosition() setRailPosition() getRotaryTable() addRotaryTable() removeRotaryTable() getRotaryTableAngle() setRotaryTableAngle() getToolNames() getRobotToolInfo() attachRobotTool() detachRobotTool() setRobotToolOffset() setRobotToolColor() getTrajLineStyle() setTrajLineStyle() setTrajStepStyle() getWorkspaceObjectPos() setWorkspaceObjectPos() rotateWorkspaceObject() getWorkspaceObjectRotationAngles() setWorkspaceObjectRotationAngles() getWorkspaceObjectTransformMatrix() setWorkspaceObjectTransformMatrix() getWorkspaceObjectNormal() setWorkspaceObjectNormal() findWorkspaceObjectsByName() getWorkspaceObjectName() getWorkspaceObjectCount() getWorkspaceObjectBoundingBox() setCollisionAvoidanceParams() setCollisionAvoidanceObjects() recalculateCollisionAvoidance() restoreOriginalTrajectory() pickWorkspaceObject() dropWorkspaceObject() addRobotPosture() removeRobotPosture() updateRobotPosture() getPostureMoveType() setPostureMoveType() getMillingProgram() simulateRobotMotion() getRobotJointLimits() isPostureValid() getJointAnglesMapping() setJointAnglesMapping() getHomeJointAngles() getRobotTrajData() getRobotPostureArray() getRobotCurrentPostureIdx() getSimulationVelocity() sendDataToSerialPort()

A complete worked example ships as plugins/brw-plugin.js. Commands 1 and 2 draw rectangles and circles in multiple passes down the Z-axis (with the singular poses of the path marked on the line), command 3 takes the milling program generated in Mill / Sculpt with getMillingProgram() and adds a posture that runs it (MoveType.MILL), and userAction() shows how a posture's plugin action receives the G-code text.

smart_toy

AI Assistant

AI-Powered BRW

BabaCAD Robotics Web is one of the first industrial robot simulation and programming applications with artificial intelligence built in. The AI Assistant sits beside the 3D workspace: you describe in plain language what the robot should do, or ask how something in the application works, and it answers with an explanation or with a ready program for the simulator.

How It Works

  • 1. You ask. For example: "Move the robot from the initial pose to [1000, 0, 0] with normal [0, 0, -1] along a line", or "How do I add a linear rail?"
  • 2. BRW adds what the model needs to know. With your question it sends the relevant parts of this documentation, the plugin API functions and a demo plugin, so the answer follows the real API of the application.
  • 3. The language model answers. The request goes to our own model server; a how-to question gets an explanation, a task gets JavaScript plugin code.
  • 4. The task becomes a simulation. With Save and run the code is saved as brw-plugin.js, loaded and run once: it defines the postures and movement types in the queue, and you press Play to watch the robot do it.

Type /demo to try a ready-made request, and /repeat to send the last request again.

The AI Assistant in BabaCAD Robotics Web: a task typed in plain language, the generated plugin code and the robot simulation

AI Credits (Tokens) And How To Get Them

The assistant is used with an activation code, and every code carries a balance of tokens, the AI credits. Each request spends tokens: your question, the knowledge sent with it and the answer all count, so a task that needs the API functions costs more than a short how-to question. The panel shows the tokens left and the cost of the last request; when the balance is used up, the assistant stops answering until the code is topped up.

The AI Assistant is in an experimental phase and currently available to educational institutions, mainly teachers and professors. To get an activation code with credits, or more credits for a code you have, click Activate... and then Request Activation Code, or write directly to support@babacad.com and tell us who you are and where you teach.

Fair Use

Requests are paced (a short pause between two requests and a limit per hour), and questions are limited in length. Violent or spam requests and bot-like request rates put a code on a blacklist, after which it cannot be activated. With your consent, given in the window shown after activation, your questions and ratings are collected to improve the assistant; without it they stay in your browser.

The Panel

The AI Assistant is docked at the right edge of the workspace. Click its header or the arrow to collapse it to the title bar, and the ✕ to hide it. Settings → Show AI Assistant shows or hides the panel (the ✕ unchecks it). By default the panel is visible but collapsed to its title bar; afterwards it remembers the state you left it in.

Activation

Activate... at the top of the panel opens a window for your activation code. The code is sent with every request and the service checks it and counts the tokens used against it. It is kept for the current session only, unless Remember on this device is checked (then it is stored in this browser); Deactivate forgets it. Once the code is accepted a window suggests starting with the /demo prompt, a ready-made task request (type /demo and send it, or use its button); /repeat sends the last request again. Nothing is sent before a code is entered. Abusive use (violent or spam requests, bot-like request rates) puts a code on a blacklist: such a code cannot be activated, and a window names the reason and the support address. Request Activation Code, under the code field in that window, explains how to get one: the AI Assistant is an experimental feature, available to educational institutions (mainly teachers and professors), and a code is requested by email from support@babacad.com. Under the button the panel shows the tokens left for your code and the cost of the last request; the same line is in the activation window. The numbers come from the service with every answer, so they appear after the first request.

Asking

Type a question and press Enter or the send button (Shift+Enter starts a new line). The answer appears in the panel below your question; errors from the service, such as a wrong code or used-up tokens, are shown there as well. Code in an answer is shown in a box with a Copy button. Under each answer the panel asks, optionally, how the assistant is doing: 1 Bad, 2 Fine, 3 Good or 0 Dismiss (buttons, or the digit typed into the prompt; Dismiss hides the question for the session). Ratings stay in your browser unless you have allowed their collection: the window shown after activation asks whether your questions and ratings may be collected to improve the assistant, with a checkbox (unchecked by default, changeable any time through Activate...); only with it checked are ratings also sent to BabaCAD. An answer rated Good is remembered with its request, and when a later task resembles it, that answer is shown to the model as the pattern to follow, which makes answers to recurring tasks more consistent.

What The Assistant Knows

With every question the assistant reads three files and sends what the question needs to the model as its system prompt, in English: this documentation as the Markdown file docu/brw-help-ai.txt (made from this page by scripts/help2text.py, which drops the sections the assistant does not need, such as this one; brw-help.txt is the full page, and the page itself is converted in the browser when neither file is there), the plugin API functions with their syntax (plugins/apiFunctions.js) and the demo plugin (plugins/brw-plugin.js). A task (writing a plugin, making the robot do something) gets all API functions with their syntax and the demo plugin, plus the parameter descriptions of the functions the task touches; every question gets the documentation sections that match its words, and a how-to question that matches nothing (one not asked in English, for example) gets the whole documentation. It can therefore explain the application and write plugins: generated code uses the API syntax, follows the structure of the demo plugin and comes as JavaScript inside a code block. The panel says what was sent and how large it is, since it counts towards the tokens of the request. If the model's context window is too small for all of it, the assistant says so and asks again with the part the question needs: the API functions and the demo plugin for a task, the documentation for a how-to question.

Saving A Task As The Plugin

When an answer contains code for a task (for example "move the robot from the initial pose to [0, 1000, 0] along a line"), the panel asks whether to save it as brw-plugin.js and run it. Save and run writes the file, loads it as the application's plugin and runs the code once: that defines the task (the postures in the queue), and the panel answers Task is defined, just click the Play >> button to run the simulation. When you ask for a plugin or a user command explicitly, the panel instead asks which user command (1, 2 or 3) should run the code; Save and load plugin writes and loads the file, and clicking that user command runs it. The file goes into the workspace folder: after an export to a folder in this session it is written there directly; after an import the browser asks you to choose the workspace folder and to allow writing (a web page cannot write into a folder on its own); without a workspace you are asked for any folder. Browsers without folder access (Firefox, Safari) download the file instead and the plugin is loaded all the same. A plugin that was already loaded is kept: its commands that are not reassigned still run, and each saved task replaces only its own command. Code that does not compile is not saved. A workspace export now includes the loaded brw-plugin.js.

terminal

Console

Plugin Output

Anything a plugin passes to logMessage() is printed to the console docked along the bottom of the workspace, so you can follow a run without opening the browser's developer tools.

brwAPI.logMessage('Pass ' + i + ' of 4 complete');

Objects and arrays are formatted as JSON, everything else is printed as text. Each line is timestamped and the view follows the newest entry.

Controls

The console is always docked and starts collapsed to its title bar. Click the bar, or the chevron, to expand it.

expand_less Expand / minimize
Toggles the log view.
delete Clear
Empties the log.

While collapsed, incoming messages raise a counter on the title bar rather than opening the panel, so a running plugin never pulls focus from the scene. The most recent 500 lines are kept.

cable

Serial Port

Talking To Real Hardware

A plugin can push data out over a serial connection while the simulation runs, so the same routine that moves the arm on screen can drive a real controller, a gripper, or any device listening on the port.

Connecting

Open settings Settings and press Serial Configure to choose a port. Until a port is connected nothing is transmitted, and calls to the API are simply ignored.

cable Serial Configure
Pick and open the port.
Serial Configure in the settings window

Sending From A Plugin

One call does the work. It takes a string, so format the payload however the device on the other end expects, including any line terminator.

// stream the live joint angles out as degrees
const q   = await brwAPI.getCurrentJointAngles();
const deg = q.map(a => (a * 180 / Math.PI).toFixed(2));

await brwAPI.sendDataToSerialPort(`J ${deg.join(' ')}
`);

Combined with simulateRobotMotion, this lets a plugin play a path in the viewport and mirror it to hardware in the same pass.

settings

Global Settings & Support

Language Support

BabaCAD Robotics supports a wide variety of localizations, accessible via the language icon:

English French German Chinese Japanese Spanish Italian

Configuration & Info

Velocity: Adjust movement speed slider.

Show Pivot/Path: Toggle visualization of the robot's gizmo and trajectory path.

Dark Mode: Switches the whole interface between the light and dark themes. Your choice is remembered on this machine and restored on the next visit.

Center the view: Resets the view center (to the origin for one arm, to the middle of the arms for several) and the zoom to the robot model's defaults, after panning with Shift / Alt + drag or zooming with the wheel.

Info: Access system versioning and the Discord Link for support.