Navigate

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
  1. The user authenticates with Cephable via OAuth
  2. Your app creates a User Device on behalf of the user via the Cephable API
  3. Your app creates a Device Token for that device
  4. Your app connects to the Cephable Device Hub with the device token
  5. The Cephable app sends commands (face expressions, voice, gestures, virtual buttons) — your app receives them via the DeviceCommand event

Prerequisites

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 server
  • signalr (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