SkillsCapabilitiesExploreContact
Projects
Staübli Reborn: Industrial Robot with a Modern Control System
Transport & Rigging
Motivation
Fabricating the Base
Framework & Dependencies
Implementation
Operating System & Deployment
Prototype Software
Boot Disk SD Emulation
thumbnail

Framework & Dependencies

2025-3-4
4th article in Staübli Reborn: Industrial Robot with a Modern Control System
Skill
Software
Robotics
Javascript
Project
Staubli
Summary:
  • Built an 800-line frontend framework using signals and Web Components to implement functional reactive programming for state-driven UI updates
  • Eliminated build tools and npm by using browser importmaps for dependency resolution and JSDoc annotations for compile-time type checking
  • Vendored three.js, URDF loader, and inverse kinematics libraries directly into source control with reproducible curl-based download scripts
  • Refined a Staubli TX90 URDF robot model in FreeCAD to achieve geometric accuracy for real-time 3D inverse kinematics visualization

Dependencies

An important and early decision in any software project are foundational dependencies. They are present for so long that they tend to leak abstractions into the rest of the code, and in practice I’ve found it’s easier to lean INTO this rather than try to add extra layers of indirection.

Webserver Implementation

The python server uses only two dependencies for technical protocol implementations with minimal abstraction:

  • pyserial
  • websockets

Framework Implementation

In order to avoid including too many dependencies I built a quick frontend framework (whoops!)

  1. State management using signals - 300loc
  2. Components using webcomponents - 500loc

It has been very productive, though in a professional setting I would tend towards using codebases with existing documentation and support rather than writing myself.

The basic theory is:

  1. Signals are a function that can be called to retrieve the value, and a function to set the value
  2. Effects call these functions to get the signal values, and perform side effects
  3. When an effect is created it tracks which signals it calls
  4. When a signal is updated it invokes all relevant effects

The component system is similar:

  1. Each component has a tag that defines where it is placed
  2. Each component has a template that is rendered inside the tag
  3. Each component has an effect that produces a map of css selector to desired state
  4. When the effect is called:
  5. Each key in this map is looked up in the template and matched to a list of elements
  6. The attributes, properties, and events of each matched element are updated

This is the core loop of Functional Reactive Programming, the foundational theory behind modern browser based frontend frameworks. It is surprising how little code it actually takes to implement, and in this exercise I’ve learned quite a bit of

Dependencies

A major pain point when working on javascript projects is the dependency management ecosystem. There are so many people creating so many tools that the goal seems to be missed. I don’t want to spend my time building code, I want to spend it writing my logic!

From this principle I decided not to have any build system, and not to use any npm.

Types are Good

The lack of a build system meant that the browser has to directly execute the code I write, which eliminated typescript. Luckily typescript can still be run during development! I installed tsc directly into the development environment, and wrote types into jsdoc comments. This allowed the computer to catch a large class of errors

/**
 * @implements {RobotInterface}
 */
export class RobotPreview {
  /**
   *
   * @param {RobotControl} control
   * @param {RobotState} initialState
   */
  constructor(control, initialState) {
    this.control = control;
    /** @type readonly [() => RobotState, (newState: RobotState) => void] */
    const [state, setState] = createSignal(initialState);
    this.state = state;
    this.setState = setState;
    this.name = "preview";
  }
  ...

Dependencies are Still Useful

I recently went through the process of writing a 3d renderer for the browser directly, and it was not worth the effort. Instead I decided to use three.js which presents an issue because it is distributed as many interdependent files.

Modern browsers have a solution for this, the importmap lets us map names to paths that the browser can use to look up source files.

<script type="importmap">
  {
    "imports": {
      "three": "/js/vendor/three/three.js",
      "three/": "/js/vendor/three/",
      "urdf-loader/": "/vendor/js/urdf-loader/",
      "closed-chain-ik-js": "/vendor/js/closed-chain-ik-js.js",
      "gl-matrix": "/vendor/js/gl-matrix.esm.js",
      "linear-solve": "/vendor/js/linear-solve.esm.js",
      "svd-js": "/vendor/js/svd-js.esm.js"
    }
  }
</script>

In code I control it is possible to use relative imports, but this allows me to vendor (include in my source control) dependencies directly:

staubli/html/vendor/js/urdf-loader/URDFLoader.js

import * as THREE from 'three';

import { STLLoader } from 'three/examples/jsm/loaders/STLLoader.js';

Causes the browser to fetch http://domain.tld/js/vendor/three/examples/jsm/loaders/STLLoader.js

So as long as I can put the files in the right places it is simple to find.

I’m concerned with ensuring that these files have a known providence and can be recreated or updated, so I added a shell script to re-download them:

...
curl "https://cdn.jsdelivr.net/npm/linear-solve@1.2.1/+esm"  --output "$VENDOR_DIR/js/linear-solve.esm.js"
curl "https://cdn.jsdelivr.net/npm/gl-matrix@3.4.3/+esm" --output "$VENDOR_DIR/js/gl-matrix.esm.js"
...

URDF?

The Unified Robot Description Format is a standard way to describe the kinematics of robots, including joints, constraints, collisions, and visualization. Two projects really helped me out, the URDFLoader which takes the path to a urdf file and loads it into a three.js scene, and closed-chain-ik-js which uses the same structure to perform inverse kinematics (determine joint angles in order to position the end effector in a certain place)

The publicly available Staubli TX90 URDF definition is close enough to be useful, but isn’t exactly the same. A few hours in FreeCAD and some reference to the manual allowed me to update it to an exact geometric match and an approximate visual match

Result

With a predictable way to include a very small and stable set of dependencies I am free to write code knowing that no matter what happens to any of those projects my code will not break. In the future it doesn’t matter if the ecosystem has moved from webpack to vite to esbuild, and it doesn’t matter if the author of left-pad removes their repository and causes a cascading chain of dependencies to fail to resolve.

Previous Next
Featured Work
CNC Surface Grinder RetrofitWelding PositionerStaübli Reborn: Industrial Robot with a Modern Control System
Company Info
About UsContact UsPrivacy Policy
Specific Solutions LLC
Portland, OR