Building with BYOBrain
A guide to building resilient, non-blocking games powered by local AI.
Because Bring Your Own Brain (BYOB) runs on the player's local hardware (via browser extension or web fallback), inference speeds can vary wildly from high-end GPUs to integrated mobile chips. Designing for BYOB requires a shift in mindset: the game must never wait for the AI.
The Golden Rules of BYOB Design
1. Fallback is King: Your game must be 100% playable without AI. If the model is slow, offline, or produces gibberish, the game should instantly fall back to standard scripted lines.
2. The Player Always Wins: Player actions (closing a menu, walking away, starting combat) must immediately interrupt and cancel any background AI generation to save GPU compute.
3. Never Block the Event Loop: The game state should continue ticking while the AI thinks. Wrap requests in timeouts.
1. Basic Setup & Mounting
Load the V2 SDK script and instantiate the brain. You can mount the connection UI anywhere in your DOM. The SDK automatically detects if the user has the high-performance BYOB Extension installed, or safely falls back to the web iframe.
<!-- Load the SDK -->
<script src="https://bringyourownbrain.org/byob-web-sdk-v2.js"></script>
<!-- Container for the connect button -->
<div id="connector-slot"></div>
<script>
const brain = new BringYourOwnBrainWeb({
container: document.getElementById('connector-slot'),
width: '210px',
height: '40px',
onState: (state, msg) => {
if (state === 'ready') console.log('Brain connected!');
}
});
brain.mount(); // Renders the iframe
</script>
2. The Non-Blocking Request Pattern
Do not just await brain.chat(). Instead, wrap requests with a Promise.race timeout so your game doesn't hang if the user's GPU is overloaded.
/**
* Safe, cancellable wrapper for AI requests.
* @param {Array} msgs - Messages array
* @param {Object} opts - BYOB options (priority, max_tokens, etc)
* @param {Number} timeoutMs - Max time to wait before bailing
*/
function askBrainSafe(msgs, opts, timeoutMs = 8000) {
const result = { cancel: () => {}, promise: null };
try {
// 1. Initiate request
const p = brain.chat(msgs, opts);
// 2. Expose the native cancel method
result.cancel = () => { try { p.cancel(); } catch(e){} };
// 3. Setup Race Condition for Timeout
let timer;
const to = new Promise((_, reject) => {
timer = setTimeout(() => {
result.cancel(); // kill generation
reject(new Error('timeout'));
}, timeoutMs);
});
// 4. Return the raced promise
result.promise = Promise.race([p, to]).finally(() => clearTimeout(timer));
} catch(e) {
result.promise = Promise.reject(e);
}
return result;
}
3. Priorities & Compute Management
The BYOB engine supports a queue system based on priority. Sending a high priority task instantly preempts and pauses lower priority tasks.
- high Direct Player Interaction: The player clicked a character and is waiting for a response. Maximize tokens/speed.
- normal Reactions: The player bumped into an NPC or triggered a quick event.
- low Ambient Chatter: Background bots talking to each other. Will be dropped automatically if a high priority task comes in.
// The player is waiting for this:
const reply = brain.chat(messages, {
priority: 'high',
max_tokens: 40,
temperature: 0.8
});
// A background NPC is just thinking:
const ambient = brain.chat(messages, {
priority: 'low',
max_tokens: 20
});
4. Cancellation (Stop the GPU)
If an NPC is talking or generating text, and the player walks away or attacks them, you must cancel the promise. This frees up the user's hardware immediately.
let currentAiTask = null;
function chatWithNPC(npc, text) {
// If they were already generating something else, kill it!
if (currentAiTask) {
currentAiTask.cancel();
}
currentAiTask = askBrainSafe([...], { priority: 'high' }, 5000);
currentAiTask.promise
.then(reply => npc.say(reply))
.catch(err => {
// Handle timeout or manual cancellation gracefully
if (err.cancelled) return;
npc.say("I lost my train of thought..."); // Fallback!
})
.finally(() => currentAiTask = null);
}
// When the player triggers combat:
function startCombat() {
if (currentAiTask) currentAiTask.cancel();
}
5. Pre-cooking & Caching (The Ambient Secret)
Don't generate ambient idle lines on the fly. When the game engine detects the player is idle, use low priority to "pre-cook" lines and store them in an array. When an NPC needs to speak idly, pop a line from the array instantly.
// Run this inside your game tick/loop:
function ambientTick() {
// Only generate if we aren't busy and the player hasn't done anything recently
if (isAiBusy || playerIsActive) return;
const idleNPCs = getNPCsWithEmptyCache();
if (idleNPCs.length > 0) {
const target = idleNPCs[0];
// Generate a line in the background
const task = askBrainSafe(
[{role: 'system', content: 'Generate a short idle thought.'}],
{ priority: 'low' },
12000 // give it plenty of time
);
task.promise.then(line => {
if (validateLine(line)) {
target.dialogueCache.push(line); // Store for later!
}
}).catch(() => { /* quietly fail */ });
}
}
// When the NPC actually needs to speak on screen:
function npcIdleSpeak(npc) {
if (npc.dialogueCache.length > 0) {
npc.say(npc.dialogueCache.shift()); // Instant delivery!
} else {
npc.say(npc.defaultScriptedLines[0]); // Scripted fallback
}
}
6. Data Validation
Local AI models hallucinate. They might spit out Markdown, empty brackets, or system prompts. Never pipe AI text directly to your UI without cleaning it first. If validation fails, throw it away and use a scripted line.
function cleanAiText(raw) {
// Remove HTML tags, markdown artifacts, and trim
let s = String(raw || '').replace(/<[^>]*>/g, '').trim();
// Remove common AI prefix bugs (e.g., "Bot Name: ")
s = s.replace(/^(NPC|Bot|Assistant)\s*:\s*/i, '');
return s;
}
function isValidLine(s) {
if (!s || s.length < 2 || s.length > 200) return false;
// Catch common model breakages
if (/\[error|
API Reference Overview
new BringYourOwnBrainWeb(options)
Creates the SDK instance.
container: DOM element to mount the iframe button in.width/height: CSS strings for the iframe dimensions.onState(state, message): Callback triggered on state changes ('idle','ready','error').
brain.chat(messages, options) -> Promise
Sends a chat completion request to the connected model.
messages: Array of objects[{role: 'system', content: '...'}, {role: 'user', content: '...'}]options.priority:'high','normal', or'low'.options.max_tokens: Integer limit for the response.options.temperature: Float between 0.0 and 2.0.options.onChunk(text): Callback for streaming tokens in real-time.
Returns: A Promise that resolves to a string. The Promise object has an attached .cancel() method to abort the request.
brain.open()
Programmatically opens the BYOB hub connection modal without requiring a click inside the iframe. Best used alongside custom UI.