Virtual Controller Integration
Enable accessible gameplay through Cephable's personal assistant adaptive controls using the Cephable Device Hub with samples in C#/.NET, JavaScript, and C++.
Overview
The Cephable Virtual Controller lets players use Cephable's personal assistant adaptive controls — face expressions, head movement, voice commands, and custom switches — to play your game. All input processing runs on-device inside the Cephable app; your game receives only the resulting command strings via a SignalR hub. This makes your game accessible to players who can't use a standard controller without requiring any changes to your core input handling.
There are samples available in C#/.NET, JavaScript (Browser), Android (Kotlin), and iOS (Swift).
Sample repository: github.com/Cephable/Cephable-VirtualController-Sample
How it works
Cephable Mobile App (face expressions, voice, gestures, virtual buttons)
│
│ (Internet)
▼
Cephable Device Hub (SignalR at https://services.cephable.com/device)
│
│ SignalR connection (device token)
▼
Your Game / Application
- The user authenticates with Cephable via OAuth
- Your app creates a User Device on behalf of the user via the Cephable API
- Your app creates a Device Token for that device
- Your app connects to the Cephable Device Hub with the device token
- The Cephable app sends commands (face expressions, voice, gestures, virtual buttons) — your app receives them via the
DeviceCommandevent
Prerequisites
- Start a free 30-day trial at services.cephable.com/trial/developers to receive your OAuth Client ID, Client Secret, and Device Type ID — no credit card required
C# / .NET Integration
The .NET sample uses Microsoft.AspNetCore.SignalR.Client:
<!-- .csproj -->
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="7.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
Minimal C# example (from sample)
This is the complete working sample from Cephable-VirtualController-Sample/src/dotnet:
using Microsoft.AspNetCore.SignalR.Client;
Console.WriteLine("Enter your device token (get this from the Cephable API at https://services.cephable.com/swagger): ");
string deviceToken = Console.ReadLine();
var connection = new HubConnectionBuilder()
.WithUrl("https://services.cephable.com/device", options =>
{
options.AccessTokenProvider = () => Task.FromResult(deviceToken);
options.Headers.Add("X-Device-Token", deviceToken);
})
.Build();
connection.On<string>("DeviceCommand", (command) =>
{
Console.WriteLine("Received command: " + command);
});
await connection.StartAsync();
// Identify the device to the hub
await connection.InvokeAsync("VerifySelf");
Console.WriteLine("Connected to hub. Waiting for commands...");
Console.WriteLine("Press enter to exit.");
Console.ReadLine();
Getting a device token: Use the Swagger UI to create a user device and generate a token, or use the browser sample to walk through the full OAuth + device creation flow.
JavaScript / Browser Integration
The browser sample uses the SignalR CDN and axios. Run npm run start from the browser folder to start a local server at http://localhost:3000:
// From Cephable-VirtualController-Sample/src/browser/index.html
const apiBaseUrl = "https://services.cephable.com/";
// Step 1: Redirect user to Cephable login
const signIn = async () => {
const clientId = document.getElementById("client_id_field").value;
localStorage.setItem("clientId", clientId);
const redirectUri = window.location.origin + window.location.pathname;
const authUrl = `${apiBaseUrl}signin?redirect_uri=${encodeURIComponent(redirectUri)}&client_id=${clientId}`;
window.location.href = authUrl;
};
// Step 2: Exchange auth code for access token
const getAccessToken = async (code) => {
const redirectUri = window.location.origin + window.location.pathname;
const response = await axios.post(
`${apiBaseUrl}signin/token?client_id=${clientId}&code=${encodeURIComponent(code)}&redirect_uri=${encodeURIComponent(redirectUri)}&grant_type=code`
);
return response.data.access_token;
};
// Step 3: Create a virtual user device
const createUserDevice = async (deviceTypeId) => {
const response = await axios.post(
`${apiBaseUrl}api/Device/userDevices/new/${deviceTypeId}?name=Web-Sample`
);
return response.data;
};
// Step 4: Create a device token for the hub connection
const createUserDeviceToken = async (userDeviceId) => {
const response = await axios.post(
`${apiBaseUrl}api/Device/userDevices/${userDeviceId}/tokens`
);
return response.data.token;
};
// Step 5: Connect to the device hub and listen for commands
const startSignalRConnection = async () => {
const connection = new signalR.HubConnectionBuilder()
.withUrl(`${apiBaseUrl}device`, {
accessTokenFactory: () => deviceToken,
headers: { "X-Device-Token": deviceToken },
})
.withAutomaticReconnect()
.build();
connection.on("DeviceCommand", function (command, macro) {
console.log("Received command: " + command);
console.log("Received macro: " + JSON.stringify(macro));
});
await connection.start({ withCredentials: false });
// Identify device to hub
const device = await connection.invoke("VerifySelf");
console.log("Connected. Device:", device);
};
C++ Integration
The C++ demo does an end-to-end flow: OAuth via a local localhost callback, device registration, and SignalR connection using signalr-client-cpp.
Dependencies (install with vcpkg):
cpprest— for the localhost auth callback serversignalr(aspnet/SignalR-Client-Cpp) — for connecting to the device hub
See the C++ sample source for the full implementation.
Hub events reference
Incoming (hub → your app)
| Event | Payload | Description |
|---|---|---|
DeviceCommand |
(command: string, macro: object) |
User triggered an input |
DeviceProfileUpdate |
(device: UserDevice) |
User's profile changed |
DeviceSettingsUpdate |
— | Device settings changed |
DeviceType |
— | Device type information |
StartListening |
— | Hub requests device start listening |
StopListening |
— | Hub requests device stop listening |
Outgoing (your app → hub)
| Method | Description |
|---|---|
VerifySelf |
Returns current UserDevice state |
GetLatestDevice |
Fetches latest device state |
DeviceStartedListening |
Notifies hub device is ready |
DeviceStoppedListening |
Notifies hub device stopped |
Macro event types
Commands arrive with an associated macro payload. Macro event types include:
["KeyPress", "Pause", "Type", "MouseMove", "JoysticksMove", "PlayAudio",
"KeyRelease", "StopOutputs", "KeyToggle", "DeviceTypeCustomAction"]
For sample macro payloads and key/button values, see the sample-data folder.
Next steps
- Unity Integration
- API Reference
- Swagger UI — Explore the Device API
- Full sample repository