Nx10 Logo
Docs

Real-Time Indices

Lightning-fast cloud inference. Access the LFM's real-time typing analysis (GBI/BCI) from your backend or Host App.

How Real-Time Inference Works

As the keyboard quietly streams telemetry data from the device, the LFM acts as a highly-optimized inference engine. It interprets how a person feels right this millisecond by analyzing roughly 50 constructed kinematic variables (e.g., tremor amplitude, stroke speed, tap cadence, pressure variance).

Because this layer runs on our specialized infrastructure rather than the user's device, it operates with sub-120ms latency while keeping the keyboard's memory and battery footprint near zero. It produces two primary indices: the Game Behaviour Index (GBI) and the Brain Charge Index (BCI).

Game Behaviour Index (GBI)

The spectrum of engagement, mapping boredom to frustration.

Despite the name ("Game"), this index applies perfectly to app engagement. The GBI measures mechanical arousal and valence. When a user is bored or lethargic, their kinematic energy drops (slow, lazy typing, long dwell times). When frustrated or anxious, their kinematic energy spikes chaotically (hard taps, rapid backspacing, heavy pressure).

The Data Contract

GBIState
{
  "score": 0.85, 
  "category": "SevereFrustration", // SevereBoredom, MildBoredom, FlowState, MildFrustration, SevereFrustration
  "confidence": 0.92,
  "timestampUTC": "2026-03-11T14:32:00Z"
}
✅ What it CAN tell you
  • If the user is "tilt typing" (e.g., revenge trading, angry messaging).
  • The exact moment a form or checkout flow becomes overly frustrating.
  • If a user is mindlessly scrolling or disengaged.
❌ What it CANNOT tell you
  • Why they are frustrated. (Is it the app UX, or did they just spill their coffee?)
  • Long-term emotional health. GBI is strictly a measurement of the immediate present.

Brain Charge Index (BCI)

The measure of cognitive load, attention, and mental fatigue.

Every intense interaction drains cognitive capacity. The BCI looks at the degradation of precision, typo frequency, and reaction timing to estimate how much "Brain Charge" the user has left before their performance falls off a cliff or they abandon the app.

The Data Contract

BCIState
{
  "score": 0.15,
  "level": "Depleted", // Depleted, Fatigued, Optimal, Peak
  "trend": "Decreasing", // Decreasing, Stable, Increasing
  "timeInStateMs": 45000
}
✅ What it CAN tell you
  • When a user needs a breather or a prompt to take a break.
  • If your educational module is demanding too much cognitive load at once.
  • Whether a user is actively recovering while doing a passive task.
❌ What it CANNOT tell you
  • If the user is physically sleep-deprived (it measures active cognitive load, not global biology).
  • Their IQ or inherent skill level. It only measures their relative cognitive expenditure against their own baseline.

Accessing the Data

Because the Nx10 Keyboard Extension operates in a restricted iOS sandbox - and is frequently used inside other apps (like Messages or Safari) - you cannot query the keyboard directly. Instead, Nx10 provides two cloud-based integration routes depending on where your logic lives.

Route 1: Server-to-Server Webhooks

If you need to execute business logic even when the user's app is completely closed (e.g., blocking a financial trade, alerting a care team, or logging compliance data), you should use Webhooks.

You can configure the Nx10 Control Plane to fire Webhooks to your backend whenever a user crosses a critical threshold (like entering SevereFrustration or Depleted).

server.js (Node.js / Express)
const express = require('express');
const crypto = require('crypto');
const app = express();

app.use(express.json());

// Nx10 Webhook Endpoint
app.post('/api/nx10/webhooks', (req, res) => {
    // 1. Verify the webhook signature (Best Practice)
    const signature = req.headers['x-nx10-signature'];
    if (!verifyNx10Signature(req.body, signature, process.env.NX10_WEBHOOK_SECRET)) {
        return res.status(401).send('Unauthorized');
    }

    const { deviceId, eventType, payload } = req.body;

    // 2. React to Real-Time Index changes
    if (eventType === 'gbi_critical_threshold') {
        const gbi = payload.gbiState;
        
        if (gbi.category === 'SevereFrustration') {
            console.log(`User ${deviceId} is highly frustrated.`);
            // Execute business logic: e.g., introduce friction into a trading flow
            tradingService.requireAdditional2FA(deviceId);
        }
    }

    res.status(200).send('Received');
});

app.listen(3000, () => console.log('Listening for Nx10 Webhooks...'));

Route 2: In-App (Nx10Insights SDK)

If you want to update the Host App's UI dynamically (e.g., changing colors, skipping an ad, or offering a helpful tip) while the user is actively using your app, use the Nx10Insights SDK.

This is a lightweight, read-only Swift package. It doesn't collect any data; it simply connects to the LFM with your API key via WebSockets to fetch the user's latest emotional state and stream updates to your UI.

HostAppViewController.swift
import UIKit
import Nx10Insights

class HostAppViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 1. Authenticate the Insights SDK for the current user
        let config = Nx10InsightsConfig(
            apiKey: "sk_live_abc123",
            deviceId: UIDevice.current.identifierForVendor?.uuidString ?? ""
        )
        Nx10Insights.shared.connect(with: config)
        
        // 2. You can query the state instantly on demand...
        let currentState = Nx10Insights.shared.getCurrentSnapshot()
        if currentState.gbi.category == .severeFrustration {
            hidePaywallAndShowCalmUI()
        }
        
        // 3. ...or subscribe to live WebSocket updates as they type
        Nx10Insights.shared.observeIndices {[weak self] gbiState, bciState in
            guard let self = self else { return }
            
            if let bci = bciState, bci.level == .depleted {
                print("User's cognitive load is maxed out.")
                self.showTakeABreakPrompt()
            }
        }
    }
}