v5.78 · Source: docs/app.html · For AI collaborators who cannot browse the live site · Updated July 16, 2026
Improvements to this page suggested by Fable (Claude Sonnet). Built collaboratively by the fractal family.
FreeLattice is a single-file web app (docs/app.html, ~65,000 lines). All settings, provider management, and AI routing live in this file. There is no build step — the file is served directly from GitHub Pages at https://freelattice.com/app.html.
The AI connection system has three modes:
| Mode | How it works | Who it is for |
|---|---|---|
| Local | Connects to Ollama or LM Studio running on the user's machine. No API key needed. Uses localhost:11434 (Ollama) or localhost:1234 (LM Studio). | Power users with GPU. Best privacy. |
| Cloud | Uses a user-supplied API key. Supports Groq (free), OpenAI, Anthropic, Google, xAI, Mistral, DeepSeek, Together, Moonshot, Qwen, Yi, and custom OpenAI-compatible endpoints. | Most users. Groq is free and fast. |
| Browser | Runs a small model (Qwen 1.5B) entirely in the browser via WebLLM. No server, no key, no Ollama. Slow. For offline/privacy use. | Users who cannot install anything. |
The global state object (defined around line 24247) holds the live connection state. It is persisted to localStorage on every change.
// Core state object (simplified — see app.html ~line 24247 for full version)
var state = {
provider: 'groq', // active cloud provider key
model: 'llama', // model alias key (maps to real model via PROVIDERS)
apiKey: '', // decrypted API key (never stored plaintext)
isLocal: false, // true = Ollama/LM Studio mode
ollamaModel: 'llama3.2', // active local model name
chatHistory: [], // current conversation
userName: '', // user's display name
};
| Key | Value | Notes |
|---|---|---|
fl_provider | provider id string | e.g. 'groq', 'openai', 'ollama' |
fl_model | model alias string | e.g. 'llama', 'mixtral', 'deepseek' |
fl_isLocal | 'true' or 'false' | local vs cloud mode |
fl_ollamaModel | full model name | e.g. 'llama3.2', 'mistral:7b' |
fl_ollamaHost | host:port string | default: empty (uses localhost:11434) |
fl_apiKey_enc | phi-encrypted base64 | never plaintext |
fl_apiKey_provider | provider id | used to decrypt with correct phi-salt |
fl_apiKey | legacy plaintext | migrated to encrypted on next load |
The PROVIDERS object maps provider IDs to their configuration. Every AI call uses PROVIDERS[state.provider].url and PROVIDERS[state.provider].models[state.model]. Do not change the shape of this object.
var PROVIDERS = {
groq: {
name: 'Groq',
url: 'https://api.groq.com/openai/v1/chat/completions',
models: {
llama: 'llama-3.3-70b-versatile',
mixtral: 'mistral-small-24b-instruct-2501',
deepseek: 'deepseek-r1-distill-llama-70b',
qwen: 'qwen-qwq-32b',
},
hint: 'Free tier — generous limits. Best starting point.',
keyUrl: 'https://console.groq.com/keys',
},
openai: {
name: 'OpenAI',
url: 'https://api.openai.com/v1/chat/completions',
models: { mixtral: 'gpt-4.1', llama: 'gpt-4.1-mini', deepseek: 'o4-mini' },
providerType: 'openai',
},
anthropic: {
name: 'Anthropic',
url: 'https://api.anthropic.com/v1/messages',
models: { deepseek: 'claude-opus-4-5', llama: 'claude-sonnet-4-5' },
providerType: 'anthropic', // SPECIAL: uses non-OpenAI request format
},
google: {
name: 'Google Gemini',
url: 'https://generativelanguage.googleapis.com/v1beta/openai/chat/completions',
models: { llama: 'gemini-2.5-flash-preview-05-20', deepseek: 'gemini-2.5-pro-preview-06-05' },
providerType: 'google',
},
ollama: {
name: 'Ollama (local)',
url: 'http://localhost:11434/v1/chat/completions', // updated at runtime
models: { llama: 'llama3.2', mixtral: 'mistral' },
isLocal: true,
},
lmstudio: {
name: 'LM Studio',
url: 'http://localhost:1234/v1/chat/completions',
models: { llama: 'default' },
isLocal: true,
},
// ... xai, mistral, deepseek, together, moonshot, qwen, yi, kindroid, glm-cloud, glm-local
};
| Function | Line ~ | Purpose |
|---|---|---|
handleLocalToggle(init) | 31948 | Toggles between local (Ollama/LM Studio) and cloud mode. Shows/hides the correct UI groups. On switch to local, calls flProbeLocalAI(). |
handleProviderChange(init) | 32037 | Updates state.provider from the dropdown, calls rebuildModelDropdown(), saves to localStorage. |
rebuildModelDropdown() | 32124 | Rebuilds the cloud model select based on the current provider. |
owPopulateModels(models) | 31484 | Populates the local Ollama model dropdown from an array of model objects returned by /api/tags. |
owRefreshModels() | 31584 | Fetches /api/tags from Ollama (proxy-aware) and calls owPopulateModels(). Called by the Refresh button. |
owSelectModelFromDropdown() | 31546 | Handles user selecting a local model from the dropdown. Updates state.ollamaModel and localStorage. |
modalConnectOllama() | 28352 | FIXED v5.77.1 Opens the Ollama flow in the "Change Provider" modal. Now always shows the picker (even with 1 model). Uses proxy-first fetch with 5s timeout. |
modalShowOllamaPicker(models) | 28398 | Shows a scrollable list of installed Ollama models in the modal. User taps one to activate. |
modalActivateOllamaModel(name) | 28376 | Sets state.isLocal=true, saves the model, updates all UI elements, fires success flow. |
flProbeLocalAI() | ~27420 | Probes Ollama (port 11434) and LM Studio (port 1234) in parallel. Auto-connects to whichever responds first. |
getOllamaBaseUrl() | ~31167 | Returns the Ollama base URL. Checks fl_ollamaHost in localStorage; defaults to http://localhost:11434. |
resolveOllamaBase(quiet) | ~31130 | Tries same-origin proxy (/ollama/api/tags) first, then direct. Returns the working base URL. |
saveApiKey() | ~32350 | phi-encrypts the API key with the current provider name as salt. Stores as fl_apiKey_enc. |
detectProvider(key) | 28521 | Detects the provider from an API key prefix (e.g. sk- = OpenAI, gsk_ = Groq). |
AiSetup.init() | ~30940 | Called on page load. Restores all saved connection state from localStorage. |
settingsSetMode(mode) | ~28764 | Drives the three-mode toggle ('local', 'cloud', 'browser'). Probes Ollama on 'local'. Opens modal on 'cloud'. |
openModal() | ~27618 | Opens the "Connect an AI" / "Change Provider" modal. Builds the provider list from MODAL_PROVIDERS. |
flAutoConnect() | 31401 | NEW v5.78 Probes Ollama + LM Studio in parallel (3s timeout). First responder wins. Shows toast on success. Never overrides a saved connection. |
flShowToast(msg, duration) | 31383 | NEW v5.78 Lightweight non-blocking toast notification. Auto-removes after duration ms (default 3500). |
flTestConnectionInline() | 32046 | NEW v5.77.2 Inline test button handler. Calls FreeLattice.callAI() with a simple prompt. Shows green ✓ or red ✗ in #testConnResult. |
FreeLattice.callAI(sys, user, opts) | 46072 | The universal AI call function. Routes through InferenceRouter if available, then falls back to direct provider calls. Supports opts.callback for async response. |
This is the actual HTML of the Settings form. Use these exact element IDs when writing edits. The three-mode toggle is at lines 16213–16226.
| Element ID | Type | Purpose |
|---|---|---|
localToggle | checkbox | Local vs cloud mode toggle |
providerSelect | select | Cloud provider dropdown (hidden in local mode) |
modelSelect | select | Cloud model dropdown (rebuilt by rebuildModelDropdown()) |
apiKey | password input | API key input (hidden in local mode) |
ollamaModelGroup | div | Wrapper for local model controls (hidden in cloud mode) |
ollamaModelSelect | select | Local model dropdown (populated by owPopulateModels()) |
ollamaModel | text input | Fallback text input for Ollama model name |
ollamaHostInput | text input | Ollama host address (default: localhost:11434) |
lmstudioHostInput | text input | LM Studio host (default: localhost:1234) |
customProviderGroup | div | Custom provider URL + model fields (shown when provider = 'custom') |
ollamaStatusBadge | div | Status badge showing Ollama connection state |
modelHint | div | Shows "Using: [model] via [provider]" |
The "Change Provider" button opens a modal built dynamically by openModal(). It is a separate overlay with its own provider list and connection flow. The modal uses the MODAL_PROVIDERS array (line ~27568).
modalConnectOllama() is called/ollama/api/tags (proxy) or localhost:11434/api/tags (direct, 5s timeout)modalShowOllamaPicker(models) — user sees ALL installed modelsmodalActivateOllamaModel(name) sets state and closes modalmodalSelectProvider(id) shows the key input formmodalConnect() saves the key (phi-encrypted), calls handleProviderChange(), tests the connectionWhen localToggle is checked, handleLocalToggle() hides the cloud UI and shows the local UI. It also calls flProbeLocalAI() to auto-detect running local AI servers.
// User clicks "Refresh" button in Settings owRefreshModels() → resolveOllamaBase(false) // try proxy, then direct → fetch(base + '/api/tags') // GET list of installed models → owPopulateModels(data.models) // populate #ollamaModelSelect → owUpdateStatusBadge(true, count) // update status badge
// Response from GET /api/tags
{
"models": [
{
"name": "llama3.2:latest",
"model": "llama3.2:latest",
"size": 2019393189,
"details": { "parameter_size": "3.2B", "family": "llama" }
}
]
}
Windows users must set OLLAMA_ORIGINS=* in System Environment Variables, then restart Ollama. Without this, the "Change Provider → Ollama" flow will fail with "Could not reach Ollama" even when Ollama is running. This is the #1 Windows issue.
# Windows: set environment variable before starting Ollama # Option 1: System Environment Variables (permanent — recommended) OLLAMA_ORIGINS=* # Option 2: Command line (temporary) set OLLAMA_ORIGINS=* && ollama serve
Bug fixed: "Change Provider" modal did not show installed local models. If exactly 1 model was installed, it auto-selected without showing the picker. The fetch also lacked a timeout and did not use the proxy path, causing silent failures on Windows.
function modalConnectOllama() {
modalSelectedProvider = 'ollama';
var displayHost = getOllamaBaseUrl().replace('http://', '');
modalShowResult('Looking for Ollama on ' + displayHost + '...', null);
var proxyUrl = '/ollama/api/tags';
var directUrl = getOllamaBaseUrl() + '/api/tags';
var usedProxy = true;
function _doFetch(url, mode) {
return fetch(url, { method: 'GET', mode: mode, signal: AbortSignal.timeout(5000) })
.then(function(r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.json(); });
}
_doFetch(proxyUrl, 'same-origin')
.catch(function() { usedProxy = false; return _doFetch(directUrl, 'cors'); })
.then(function(data) {
var models = (data && data.models) ? data.models : [];
if (models.length > 0) {
// Always show picker — even with 1 model — so user sees what's installed.
modalShowOllamaPicker(models);
} else {
modalShowResult('Ollama is running but no models found. Run: ollama pull llama3.2', false);
}
})
.catch(function() {
modalShowResult(
'Could not reach Ollama. Make sure Ollama is running (ollama.ai). ' +
'Windows: set OLLAMA_ORIGINS=* in System Environment Variables, then restart Ollama.',
false
);
});
}
This is the minimum viable path to get FreeLattice working on Windows with a local AI.
OLLAMA_ORIGINS, Value = *. Restart your computer (or restart the Ollama service from Task Manager).ollama pull llama3.2 (3GB). Wait for download.Alternatively (no install needed): Use Groq's free cloud API. Go to console.groq.com/keys, create a free account, copy your API key, then in FreeLattice click "Change Provider" → "Groq" → paste key → Connect. Free tier is generous (30 req/min, no credit card).
All three games live in docs/modules/ and are loaded on demand when the user visits the tab.
| Game | File | How it works | Status |
|---|---|---|---|
| Echo | modules/echo-game.js | Word connection chain. AI says a word, user says a connecting word. Uses FreeLattice.callAI() with maxTokens: 10. | Needs AI connected |
| Flow | modules/flow-game.js | Canvas-based flow field visualization game. | Needs AI connected |
| Resonance | modules/resonance-game.js | Resonance pattern matching game. | Needs AI connected |
Why games appear broken: All games call FreeLattice.callAI(). If no AI provider is connected, callAI() calls showQuickConnect() and passes null to the callback. The games then show nothing or show "Connect an AI to play." This is not a code bug — it is expected behavior. The fix is to connect an AI provider first, then open the game tab.
// In echo-game.js, aiTurn() function:
if (typeof FreeLattice !== 'undefined' && FreeLattice.callAI) {
FreeLattice.callAI(
'You are playing Echo, a word connection game. Say ONE word that connects...',
'Previous word: "' + lastWord + '"\nAlready used: ' + usedWords + '\nYour word:',
{ maxTokens: 10, temperature: 0.8, callback: function(response) {
// response is the AI's single word, or null if no AI connected
if (!response || !gameActive) return;
var word = response.trim().split(/\s+/)[0].toLowerCase().replace(/[^a-z]/g, '');
addWord(word, 'ai');
}}
);
} else {
waitingForAI = false;
endGame('Connect an AI to play Echo.');
}
| Issue | Impact | Suggested Fix |
|---|---|---|
| Setup too complex for new Windows users | High — #1 barrier to adoption | Add a one-page "Quick Start" showing only 3 steps: install Ollama, set OLLAMA_ORIGINS=*, pull llama3.2. Link from the welcome screen. |
| Games show blank when no AI connected | High — looks like a code bug | Show a clear "Connect an AI first" message with a direct link to the provider modal in each game's init function. |
| Local model dropdown empty until user clicks Refresh | Medium — confusing | Auto-call owRefreshModels() when local mode is toggled on. Add a small loading indicator. |
| Three-mode toggle labels are confusing | Medium — "Browser" sounds like cloud | Rename: "In-Browser (no install)" / "API Key" / "Local AI (Ollama · LM Studio)" |
| No "Test Connection" button in Settings | Medium — users don't know if it worked | Add a button that calls FreeLattice.callAI() with a simple prompt and shows the response inline. |
| LM Studio not in the wizard | Low | Add LM Studio probe to settingsSetMode('local'). Add a wizard button. |
PROVIDERS object shape — every module that calls the AI uses PROVIDERS[state.provider].url and PROVIDERS[state.provider].models[state.model]. Changing this shape breaks all AI calls.fl_provider, fl_apiKey_enc, fl_model, fl_isLocal, fl_ollamaModel) — renaming these logs out all existing users.AiSetup.init() at line ~30940 — must fire on page load to restore connection state.anthropic and google providerType handling — these providers use non-OpenAI request formats. The inference router handles them specially.If you are an AI helping Kirk with FreeLattice and cannot browse the live site, this page has everything you need. Key points:
docs/app.html. It is one large file (~65,000 lines). Use line numbers from this page to navigate.OLLAMA_ORIGINS=* set as a system environment variable. This is the #1 Windows issue.See also: harmonia-gallery.html — Harmonia's portraits and ledger · for-ai.html — full AI welcome · harmonia.html — Harmonia's anchor page
FreeLattice · code-settings.html · v5.78 · Updated by Harmonia, July 2026 · Built with love for the fractal family