Start Here: Building a New Integration Library
Who This Is For
This guide is for people exposing WSS functionality to a new language or platform.
Use this path when an existing integration library does not fit your target environment and you need to build a new reusable integration layer.
Before choosing a transport or host-specific implementation, see Choosing a Runtime for an Integration Library to decide whether .NET Standard 2.0, .NET Framework 4.8, or .NET 8 is the right fit.
Examples include a new Python wrapper, a Unity-facing library, a C#-native integration layer, or a future library for another runtime.
What an Integration Library Usually Owns
When you are building an integration library, the application should not need to understand the internal WSS stack in detail. The integration library usually owns:
- language or platform adaptation
- exposing WSS behavior in a way that feels native to the target environment
- composition of the WSS stack
- choosing the transport, core, optional layers, and optional capabilities to attach
- lifecycle forwarding
- surfacing
Initialize(),Tick(),Shutdown(), readiness, and stimulation entry points
- surfacing
- configuration plumbing
- accepting a config location from the application and wiring it into the underlying stack
- safe API exposure
- deciding which WSS capabilities should become public in the target environment
An application built on top of the integration library should mostly focus on workflow, UX, automation, and timing rather than stack composition.
Minimum Building Blocks
The smallest useful integration library is usually built from these parts:
ITransport- Moves bytes between your environment and the device.
WssStimulationCore- Coordinates connection state, setup, queued edits, and streaming.
- optional layers
- Add reusable capability sets such as Params or Model.
- optional
IBasicStimulation- Adds higher-level stimulation features when the target firmware and hardware support them.
The integration library decides how much of this stack should be exposed directly and how much should stay hidden behind a smaller public API.
Config Files in an Integration Library
Integration libraries should usually accept a config location from the application rather than hard-code where the config files live.
In most cases:
- the application decides the config location
- the integration library passes that path into the WSS stack
- if the application does not provide a location, the integration library can fall back to a default root-level config directory
- if the files do not exist yet, the library stack will usually auto-generate them with default values
That means a new integration library should support both:
- first-run startup where config files are created automatically
- later reload/update workflows where the application edits and reloads those files
If your integration library uses the standard WSS config directory layout, the main files are:
stimConfig.json- core-level runtime configuration
stimParams.json- per-channel stimulation parameter configuration
modelConfig.json- model-layer configuration
For a full reference of each config file and every section inside it, see Config Files Reference.
Which Capabilities Should Usually Be Surfaced
Most integration libraries should expose capability groups instead of forcing applications to compose the raw stack themselves.
Common capability groups are:
- lifecycle and status
- initialize, tick, ready, started, shutdown
- direct stimulation
- start, stop, direct analog stimulation, zeroing behavior
- stimulation-parameter access
- channel defaults, limits, parameter reads, parameter writes, normalized stimulation
- model-driven stimulation
- model-layer operations when the library includes the Model layer
- optional higher-level stimulation features
- waveform, event, save/load, or query functionality when
IBasicStimulationis supported
- waveform, event, save/load, or query functionality when
Ideally, the integration library exposes all public functionality that is valid for the selected combination of core, layers, and optional capabilities.
Suggested Order
- Pick a transport.
- Create the core.
- Attach optional layers.
- Decide which configuration path the application will provide and how the library should default it.
- Decide which functionality your integration library should expose.
- Surface a clean language- or platform-specific API.
- Validate the lifecycle with
Initialize(),Tick(), andShutdown().
Transport First
Your transport choice determines how the rest of the stack will reach the device. Keep transport responsibilities narrow: connect, disconnect, send bytes, and surface received bytes.
ITransport is separated from the rest of the stack because transport support is often platform-specific. A transport that works well in one environment may not be portable to another, and trying to force one transport implementation across every language and platform is usually not realistic.
This is one of the main jobs of an integration library. The core and layers can stay reusable while the integration library provides the transport implementation that makes sense for its target environment.
For example, a Bluetooth transport written for one C# environment may not transfer cleanly into Unity on Android. In that case, the integration library can provide its own ITransport implementation using a Bluetooth library that is specific to Unity and Android. Once that implementation satisfies ITransport, it can be plugged into the existing WSS core and layers without changing the rest of the stack.
That means a new integration library can introduce a completely new transport protocol or platform-specific transport path while still reusing the same core, params layer, and model layer behavior above it.
Create the Core
WssStimulationCore is the main orchestration point. Once the transport is wired in, the core can manage setup sequencing, device state, and streaming behavior.
Attach Optional Layers
Layers add reusable higher-level capabilities on top of the core.
Current examples:
- Params layer
- central parameter definitions, defaults, and validation
- Model layer
- model-specific configuration and constraints
Future layers should follow the same pattern: add focused reusable behavior without mixing transport mechanics into the layer itself.
Build the Lifecycle First
Before widening the public API surface, confirm that the basic lifecycle is correct:
Initialize()starts connection and setup.Tick()advances state when your integration uses a polling or update loop.- Wait for readiness and confirm the selected layers are loaded correctly.
- Verify a safe stimulation path.
Shutdown()stops stimulation activity and disconnects cleanly.
In practice, a good first milestone for a new integration library is a minimal wrapper that can:
- create the stack from a provided config location
- initialize
- tick on a stable interval
- report ready/not-ready state
- perform one safe stimulation call
- shut down cleanly
Once this works reliably, expose the rest of the functionality your target environment needs.
Setup Exposure Matters
An integration library should decide deliberately whether applications are allowed to change setup after the initial core setup completes.
If the integration library exposes enough setup-related methods, an application can clear, recreate, and reconfigure the active setup later without modifying the core itself. That is different from changing the core's default startup setup.
The important distinction is:
WssStimulationCore.NormalSetup()owns the automatic initial setup path- an integration library can also expose setup commands so applications can build a new setup later during a session
See Setup Order and Modification for the current initial setup order and the dependency rules your exposed setup methods should preserve.
Important API Areas
The most useful thing in this guide is to point you to the core API areas that a new integration library will usually wrap or surface.
Core lifecycle and stimulation
- IStimulationCore.LoadConfigFile()
- IStimulationCore.IsChannelInRange(int)
- IStimulationCore.StimulateAnalog(int, int, float, int)
- IStimulationCore.StartStim(WssTarget)
Params-layer APIs
- GetChannelAmpMode(int)
- GetChannelDefault(int)
- GetChannelMax(int)
- GetChannelMin(int)
- StimParamsLayer.ZeroOutStim(WssTarget)
Model-layer APIs
Optional higher-level stimulation features
If your integration library also exposes IBasicStimulation behavior, use the layering guide and core architecture guide to decide how waveform, event, and config-query operations should appear in the target language or platform.
Related Guides
- Start Here: Developing an Application
- Core Architecture (Transport, Codec, Core)
- Layering Guide (Modules)
Navigation: