Watson Speech To Text icon Watson Speech To Text — Watson Speech-to-Text ARC plugin: cloud AI transcription with configurable models, selectable VAD (Windows/WebRTC), audio capture and visualization. Try it →
 

Create

This manual walks through making a robot skill for ARCx: what a robot skill is, how to create the project, build and try it, write it, test it, and publish it to the Robot Skill Store.

It is the walkthrough, not the reference. Two companion manuals sit beside it and hold the detail:

  • RobotSkill-Services-And-Core.md - everything a worker can call: the robot controllers, video, audio, speech, movement, kinematics, navigation, depth cameras, the Robot Bus, scripting, variables, logging, configuration, and the worker overrides themselves.
  • RobotSkill-UI-Components.md - everything visual: the three UI base classes, layout, redrawing, every UC* component, Custom UI widgets.

ARCx.xml, beside ARCx.dll, is the authoritative per-member reference for both.

It describes ARCx as of October 2026 (template version 2026.09.27.00). ARCx is an ASP.NET Core Blazor Server application on .NET 10 with a MudBlazor UI. A robot skill is a .NET class library that ARCx loads at run time.


Contents

  1. What a robot skill is
  2. Before you start
  3. Step 1: Create the project
  4. The project on disk
  5. Step 2: Build and try it
  6. Step 3: Write the skill
  7. Step 4: Test it
  8. Step 5: Publish it
  9. Troubleshooting

1. What a robot skill is

A robot skill is a plugin that adds one capability to a robot project: a camera, a servo controller, a speech engine, a tracking algorithm, a scripting surface, a connection to an external system. Users add robot skills to a project from the Add Robot Skill menu, arrange their windows on the workspace desktops, configure them, and save the whole thing as an .arcx project file.

A robot skill assembly contains up to five kinds of class. ARCx finds them by base type, so their names do not matter, but the template's names are used throughout this manual.

Class Base type Required Role
RobotSkillWorker RobotSkillWorkerAbstractEntity Yes The logic. Owns state, background work, hardware access, configuration and control commands. Exactly one instance per robot skill in the project.
RobotSkillUIMain RobotSkillUIMainBaseEntity (Blazor component) Yes The window on the workspace. One instance per connected browser.
RobotSkillUIConfig RobotSkillUIConfigBaseEntity (Blazor component) No The settings page, shown when the user opens the skill's configuration.
RobotSkillUIWidget RobotSkillUIWidgetBaseEntity (Blazor component) No A compact piece of the skill the user can place on their own Custom UI screens.
Configuration none Convention The settings, serialized as JSON into the project file.

The worker is the source of truth. UI components are transient views over it: browsers connect and disconnect, and several browsers can show the same skill at once, so nothing that must persist or keep running belongs in a UI component.

ARCx drives the worker through a fixed order: constructor (services injected), Setup(), Initialize(), OnSetConfiguration(), OnProjectLoadCompleted(), then run time, and finally DisposeOverride() when the skill is removed. Section 6 says what goes in each; RobotSkill-Services-And-Core.md section 1 has the full detail.


2. Before you start

  • A Synthiam account, signed in to ARCx (Options, Account). Creating a skill registers it in the store under that account.
  • The .NET 10 SDK, and Visual Studio or any editor that builds .NET projects.
  • Optionally, an AI coding assistant. ARCx writes a prompt and an AGENTS.md for it, and an assistant connected to ARCx's MCP server can build, install and test the skill against the running ARCx for you.

3. Step 1: Create the project

In ARCx open Robot Skills, Create (the green button in the top bar). The Create tab asks for:

  • Name: 5 or more characters; letters, digits, spaces, parentheses, hyphen and period only; and it must not contain "ARCx" in any letter case. It is title-cased, and becomes the store title, the project folder name and (with spaces replaced by underscores) the namespace and assembly name.
  • Company / Organization: 5 or more characters.
  • Description: 30 or more characters. Shown in the store and the Add Robot Skill list.
  • Categories: 1 to 3, chosen from the store's live category list.
  • Agreement to the robot skill terms.

The author name comes from your signed-in account.

When you press Create, ARCx:

  1. Registers the robot skill with Synthiam and receives a store GUID. The GUID identifies your skill forever: it names the install folder, and it is what projects save to find the skill again. Nobody else sees the skill until you upload a build and make it public.
  2. Creates My Documents\ARCx Data\Robot Skill Projects\<Name>\ (~/ARCx Data/Robot Skill Projects/<Name>/ on Linux and macOS). It refuses a folder that already holds a plugin.json, before anything is registered.
  3. Writes plugin.json from the details the store returned.
  4. Unpacks the project template into the folder, with the template's namespace, GUID, plugin folder and ARCx application folder replaced by yours.
  5. Writes AGENTS.md, which explains ARCx, the project files, where the manuals and ARCx.xml are on this machine, and the rules a robot skill follows, so any AI coding assistant opened on the folder later has the platform explained to it.
  6. Opens the project folder and moves to the created page.

The created page lists what was made, the next steps, and an AI prompt built from your description. It is rebuilt from plugin.json, so you can come back to it later. If ARCx's MCP server is on, it also says how to let an AI finish the skill through MCP.


4. The project on disk

<Name>\
  <Name>.csproj
  plugin.json              store manifest; edit Version here before each upload
  AGENTS.md                the platform explained to an AI coding assistant
  _Imports.razor           the @using lines every .razor file gets
  Configuration.cs         the settings class
  RobotSkillWorker.cs      the logic
  RobotSkillUIMain.razor   the window (+ .razor.cs)
  RobotSkillUIConfig.razor the settings page (+ .razor.cs)
  RobotSkillUIWidget.razor optional Custom UI widget; delete it and the GetCustomUIWidgets() override together
  wwwroot\                 created empty; ARCx does not serve it

There is deliberately no title.png. It is the thumbnail the store shows, and it is yours to draw: a square PNG, 256x256 or larger, in the project folder. Nothing needs it until you upload; until then every build warns "This robot skill has no thumbnail".

The template compiles and runs as it is: a timer, a counter, a log, a configuration page with a text field, a number field and a servo selector, control commands, a Custom UI widget, and an MCP tool and resource. Replace the demo with your skill and keep the file layout and base classes.

plugin.json

{
  "Name": "Face Tracking",
  "AuthorName": "Synthiam Inc.",
  "CompanyName": "Synthiam Inc.",
  "GUID": "d8294feb-405a-41b6-aab1-b795db1f3b43",
  "Version": 3,
  "Description": "Track faces from any of the ARC video sources.",
  "Categories": [ "Camera" ],
  "DLLFilename": "Face_Tracking.dll"
}
  • GUID is the store GUID. Never change it.
  • DLLFilename is the assembly ARCx loads, the csproj's assembly name.
  • Version must be raised above the store's version before each upload.
  • The presence of this file in the install folder is what marks a skill as installed.

The csproj

The generated project file is the template's with four values substituted. What matters:

Property Meaning
TargetFramework = net10.0, SDK Microsoft.NET.Sdk.Razor A Razor class library.
Platforms Build AnyCPU. A robot skill is platform-agnostic managed code, and ARCx loads the same build on Windows, Linux and macOS.
EnableDynamicLoading = true Emits the .deps.json ARCx resolves your dependencies and native libraries from.
ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch = None ARCx.dll is architecture specific and your skill is AnyCPU by design. Do not "fix" MSB3270.
ARCxGUID Your store GUID. Also the install folder name.
ARCxAppFolder The folder holding ARCx.dll, ARCx.xml and ARCx.exe: the copy of ARCx that created the project. Debugging launches ARCx.exe from here.
ARCxPluginFolder Where ARCx loads installed skills from, normally C:\ProgramData\ARCx Data\Plugins.
GenerateDocumentationFile = True Ships <Name>.xml beside the DLL. ARCx reads it so your /// comments describe the configuration to the AI agent.
FrameworkReference Microsoft.AspNetCore.App Required: the Razor SDK does not add it, and ARCx's public API is Blazor typed.

The References item group names each ARCx library the skill compiles against: ARCx, MudBlazor and DeepCloner to start with, all Private=false so nothing is copied into your output. A comment beneath them lists the other libraries ARCx ships (OpenCvSharp, ONNX Runtime, NAudio, SkiaSharp, System.IO.Ports and more); copy a line out of the comment when you need one. A library ARCx does not ship is an ordinary NuGet PackageReference. RobotSkill-Services-And-Core.md section 28 has the rules, including never shipping your own ONNX Runtime.

To ship an image or other asset, embed it rather than putting it in wwwroot; RobotSkill-UI-Components.md section 17 shows how.


5. Step 2: Build and try it

Open the .csproj in Visual Studio and press F5, or build from the command line:

dotnet build "<Name>.csproj" -c Debug -p:Platform=AnyCPU

Name the .csproj on the command line: a solution file beside it spells the platform "Any CPU", and dotnet build given only the folder fails with MSB4126.

Every successful build installs the skill. The post-build step copies the output into the plugin folder:

C:\ProgramData\ARCx Data\Plugins\
  <StoreGUID>\
    plugin.json
    title.png              when the project has one
    AnyCPU\
      <DLLFilename>, its dependencies, <Name>.deps.json, <Name>.xml

Then in ARCx open Robot Skills, Add, find your skill, and add it to a project. F5 launches ARCx.exe from ARCxAppFolder with the debugger attached, so breakpoints in the worker and the Razor code-behind work.

A rebuild while ARCx has the skill loaded does not take effect. The DLL is in use, so the copy can fail (or appear to succeed) and removing and re-adding the skill keeps running the old code. Stop ARCx, rebuild, and start it again. If the build itself cannot overwrite the file, the previous instance did not unload: something is still subscribed, see section 9.


6. Step 3: Write the skill

Work through the template file by file. Each step below says what goes where and which reference section has the detail.

The worker: where your code goes

Override Put here Detail
Constructor Store the injected services. Nothing else; Properties is still null. Services 1, 2
Setup() Return the default window size and whether multiple instances and full screen are allowed. Services 1
Initialize() Create timers, executors and subscriptions, register video sources, kinematic chains or depth sensors. The skill is not in the project yet. Services 1
OnSetConfiguration() Read the saved configuration, or keep defaults when IncludesConfig is false. Services 1, 19
OnProjectLoadCompleted() Look up other robot skills, video sources, speech engines and chains by name. The only safe place to do so. Services 1, 17
GetConfigOnProjectSave() Write the configuration. Services 19
DisposeOverride() Unsubscribe from every event, stop and dispose everything Initialize() created, deregister what you registered. Services 1, 28

Services you need are constructor parameters. The list of injectable services, and which are static, is in RobotSkill-Services-And-Core.md section 2.

The configuration

Configuration.cs is a plain class of public properties, each with a default and a /// summary written for someone who has never seen the skill. Return typeof(Configuration) from GetConfigurationType() so the AI agent and other tools understand it, and mark properties that hold another skill's window title with [RobotSkillReference("kind")]. Do not rename properties once the skill is published; saved projects depend on them. See RobotSkill-Services-And-Core.md section 19.

The main window

RobotSkillUIMain.razor binds to the worker's fields and calls its methods. The worker pushes, the window never polls:

  • Call RefreshUI() on the worker whenever what the user sees changes. It reaches every browser and every Custom UI widget and is safe from any thread.
  • Put what a loop updates several times a second in a UCRefreshRegion and redraw it with RefreshUIRegion(name), so the rest of the window is not redrawn with it.
  • Skip work that only feeds the display while HasAnyUIClient is false (or the narrower HasUIClient / HasCustomUIClient), and bring the display fields up to date in UpdateMainUIStateRequested() when a window is rendered again.

RobotSkill-UI-Components.md sections 1 to 3 cover the base class, layout and redrawing; sections 4 to 14 are the components a window is built from.

The configuration page

RobotSkillUIConfig.razor edits a deep clone of the configuration and copies it back to the worker in OnSave(), so Cancel really discards. Throw from OnSave() to reject a save with a message for the user. Lay settings out in UCTitleComponentTableRowColumn rows. See RobotSkill-UI-Components.md sections 1 and 4 to 7.

Control commands

Control commands are what scripts, other skills and AI agents call on your skill by window title. Declare them in GetSupportedControlCommands() with a description, named parameters and a ReturnType, and handle them in ControlCommand(). The template has working examples. See RobotSkill-Services-And-Core.md section 16.

Optional pieces

If the skill... Do this Detail
belongs on a phone or touch panel Keep RobotSkillUIWidget.razor and GetCustomUIWidgets(); otherwise delete both. UI 16
should give AI clients a typed tool or an image Override GetMcpTools() / GetMcpResources(). Control commands reach them without this. Services 16
is a camera Register a source with VideoRouteService. Services 8
speaks Implement TextToSpeechEngineInterface on the worker. Services 10
recognizes speech Add a SpeechToTextEngineAbstractEntity subclass. Services 11
drives the robot Implement MovementPanelInterface on the worker. Services 12
moves an arm or leg to a point Register a chain with KinematicsService. Services 13
is a depth camera or lidar Register with DepthSensorService or publish to NMSService. Services 14, 26
shares data with outside programs Publish topics and services on the Robot Bus. Services 27
offers sounds, camera actions or Auto Position actions to Blockly Override GetBlocklySoundItems(), GetBlocklyCameraItems() or GetBlocklyAutoPositionItems(). Services 1, 15
serves its own images or files Override OnDynamicResourceRequest(). UI 11
draws a map, meter or plot Use UCSkillCanvas. UI 10
runs a neural network Use ARCx's shared ONNX Runtime and OnnxAccelerationService. Services 23

Before moving on, read RobotSkill-Services-And-Core.md section 28, "Rules that will bite you".


7. Step 4: Test it

Run through these before you publish. Each one catches a mistake that does not show in a quick try.

  1. Add it to a new project. It should work with the defaults, before its configuration has ever been opened.
  2. Save the project, close ARCx, reopen the project. Every setting should come back.
  3. Open the configuration while the skill is busy. Its work should pause, and Cancel should leave the configuration as it was.
  4. Open ARCx in two browsers at once. Both windows should update.
  5. Add a second instance, if AllowMultipleInstances is true. The two must not share state, logs or variable names.
  6. Remove the skill and add it again. Nothing should keep running, and the log should show no errors.
  7. Put it on a Custom UI screen, through its widget or the built-in Robot skill window widget, without opening the workspace. It should still update.
  8. Call each control command from a script, with wrong parameters as well as right ones.
  9. Run it on Linux if you can. ARCx supports Ubuntu 24.04 LTS on x64 and ARM64; a skill that calls Windows-only APIs or ships only Windows native libraries fails there.

8. Step 5: Publish it

  1. Add title.png to the project folder: square, 256x256 or larger.
  2. Raise Version in the project's plugin.json above the version in the store.
  3. Build Release: dotnet build "<Name>.csproj" -c Release -p:Platform=AnyCPU, with ARCx stopped so the DLL can be replaced. The upload packages the whole install folder, so delete any .pdb a Debug build left in Plugins\<guid>\AnyCPU\.
  4. In ARCx open Robot Skills, Manage. It lists the skills your account owns, matched against what is installed. Press Upload on yours.
  5. Write the release notes (required). They are posted on the skill's store page as what changed in this version.
  6. Tick Send control command syntax to publish your control commands as the Control Commands section of the skill's manual on synthiam.com. It replaces that section, so leave it unticked if you wrote it by hand. Reading the commands loads the skill the way a workspace does, so a skill that cannot load is stopped here instead of reaching the store.
  7. On synthiam.com, use the skill's Edit link from Robot Skills, Manage to write its manual, add screenshots, and make it public when it is ready.

ARCx checks the package before it sends anything and says what is missing: a version not newer than the store's, no build output, a DLLFilename that is not in the build, no title.png, or a plugin.json without a name, description or 1 to 3 categories.

Users see the update in Add Robot Skill when the store version is newer than the one they have installed.


9. Troubleshooting

The name is refused with a message about "ARCx". A skill name cannot contain "ARCx" in any letter case. Choose another.

The skill does not appear in Add Robot Skill. Check C:\ProgramData\ARCx Data\Plugins\<guid>\plugin.json exists and that the AnyCPU folder holds the DLL named in DLLFilename.

"Unable to find RobotSkillWorkerAbstractEntity in assembly". The assembly has no public class deriving from the worker base, or a dependency failed to load. If the message says the type derives from "a different copy of it", the skill is shipping its own ARCx.dll: set the ARCx reference to <Private>false</Private> and rebuild. The ARCx log has the inner exception.

"Robot Skill is not supported by this platform". No folder matches the running platform and there is no AnyCPU folder.

The build succeeded but ARCx still runs the old code. ARCx had the DLL loaded. Stop ARCx, rebuild, start it again.

The DLL is locked and the build cannot overwrite it. A previous instance did not unload: something is still subscribed or a timer is still alive. Fix DisposeOverride(). Restarting ARCx releases it.

It works with one instance and fails with two. Each instance loads its own copy of every bundled DLL, and some libraries find themselves by name across the process. See RobotSkill-Services-And-Core.md section 1, "Loading and removal".

NullReferenceException in the constructor or Setup. Properties is not assigned yet. Move the code to Initialize().

Another skill is not found in Initialize. It may not be loaded yet. Move the lookup to OnProjectLoadCompleted().

Only one browser updates. You called Blazor's StateHasChanged instead of the base class one, or you are holding one component instead of calling RefreshUI() on the worker.

A Custom UI widget does not update, or nothing updates until the workspace is opened. You are calling CurrentRobotSkillUIMainComponent?.StateHasChanged(), which is null until a browser renders the skill's own window. Call RefreshUI().

The window shows stale values when it is opened. The worker skips display work while HasUIClient is false. Set the display fields in UpdateMainUIStateRequested().

The config page save is ignored. OnSave() must copy the edited clone back to the worker's live configuration.

The AI agent guesses at my settings. It sees no schema. Return the type from GetConfigurationType(), make sure <Name>.xml sits beside the DLL in the install folder, and give every property a summary.

The camera image never appears. The source name in the configuration does not match a registered source. Names are the camera skill's window title; default to the first source in OnProjectLoadCompleted().

Upload says the robot skill could not be loaded. Reading the control commands loads the skill exactly as a workspace does. Usually a NuGet package or DLL the skill uses is not shipped next to it, or an ARCx reference has Copy Local set to true. The ARCx log has the detail.

MSB3270 architecture mismatch warning. Expected. The template disables it for a reason; do not change the platform to x64 to silence it.