FreeLattice — Settings & Provider Code Mirror

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.

Contents
  1. Architecture Overview
  2. State Object & localStorage Keys
  3. PROVIDERS Object
  4. Key Functions Reference
  5. HTML Elements Reference
  6. Change Provider Modal
  7. Local Mode (Ollama / LM Studio)
  8. Bug Fix: v5.77.1 — Models Not Showing
  9. Windows Setup Guide (Under 10 Minutes)
  10. Games: Echo, Flow, Resonance
  11. Open Issues & What to Build Next
  12. Sacred Paths — Do Not Change

1. Architecture Overview verified against app.html · July 16, 2026

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:

ModeHow it worksWho it is for
LocalConnects 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.
CloudUses 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.
BrowserRuns 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.

2. State Object & localStorage Keys verified · July 16, 2026

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
};

localStorage Keys — Do Not Rename (breaks existing users)

KeyValueNotes
fl_providerprovider id stringe.g. 'groq', 'openai', 'ollama'
fl_modelmodel alias stringe.g. 'llama', 'mixtral', 'deepseek'
fl_isLocal'true' or 'false'local vs cloud mode
fl_ollamaModelfull model namee.g. 'llama3.2', 'mistral:7b'
fl_ollamaHosthost:port stringdefault: empty (uses localhost:11434)
fl_apiKey_encphi-encrypted base64never plaintext
fl_apiKey_providerprovider idused to decrypt with correct phi-salt
fl_apiKeylegacy plaintextmigrated to encrypted on next load

3. PROVIDERS Object verified ~line 24300 · July 16, 2026

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
};

4. Key Functions Reference verified · July 16, 2026

FunctionLine ~Purpose
handleLocalToggle(init)31948Toggles between local (Ollama/LM Studio) and cloud mode. Shows/hides the correct UI groups. On switch to local, calls flProbeLocalAI().
handleProviderChange(init)32037Updates state.provider from the dropdown, calls rebuildModelDropdown(), saves to localStorage.
rebuildModelDropdown()32124Rebuilds the cloud model select based on the current provider.
owPopulateModels(models)31484Populates the local Ollama model dropdown from an array of model objects returned by /api/tags.
owRefreshModels()31584Fetches /api/tags from Ollama (proxy-aware) and calls owPopulateModels(). Called by the Refresh button.
owSelectModelFromDropdown()31546Handles user selecting a local model from the dropdown. Updates state.ollamaModel and localStorage.
modalConnectOllama()28352FIXED 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)28398Shows a scrollable list of installed Ollama models in the modal. User taps one to activate.
modalActivateOllamaModel(name)28376Sets state.isLocal=true, saves the model, updates all UI elements, fires success flow.
flProbeLocalAI()~27420Probes Ollama (port 11434) and LM Studio (port 1234) in parallel. Auto-connects to whichever responds first.
getOllamaBaseUrl()~31167Returns the Ollama base URL. Checks fl_ollamaHost in localStorage; defaults to http://localhost:11434.
resolveOllamaBase(quiet)~31130Tries same-origin proxy (/ollama/api/tags) first, then direct. Returns the working base URL.
saveApiKey()~32350phi-encrypts the API key with the current provider name as salt. Stores as fl_apiKey_enc.
detectProvider(key)28521Detects the provider from an API key prefix (e.g. sk- = OpenAI, gsk_ = Groq).
AiSetup.init()~30940Called on page load. Restores all saved connection state from localStorage.
settingsSetMode(mode)~28764Drives the three-mode toggle ('local', 'cloud', 'browser'). Probes Ollama on 'local'. Opens modal on 'cloud'.
openModal()~27618Opens the "Connect an AI" / "Change Provider" modal. Builds the provider list from MODAL_PROVIDERS.
flAutoConnect()31401NEW 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)31383NEW v5.78 Lightweight non-blocking toast notification. Auto-removes after duration ms (default 3500).
flTestConnectionInline()32046NEW 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)46072The universal AI call function. Routes through InferenceRouter if available, then falls back to direct provider calls. Supports opts.callback for async response.

5. Settings Panel HTML (actual markup) verified lines 15618–15700 · July 16, 2026

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 IDTypePurpose
localTogglecheckboxLocal vs cloud mode toggle
providerSelectselectCloud provider dropdown (hidden in local mode)
modelSelectselectCloud model dropdown (rebuilt by rebuildModelDropdown())
apiKeypassword inputAPI key input (hidden in local mode)
ollamaModelGroupdivWrapper for local model controls (hidden in cloud mode)
ollamaModelSelectselectLocal model dropdown (populated by owPopulateModels())
ollamaModeltext inputFallback text input for Ollama model name
ollamaHostInputtext inputOllama host address (default: localhost:11434)
lmstudioHostInputtext inputLM Studio host (default: localhost:1234)
customProviderGroupdivCustom provider URL + model fields (shown when provider = 'custom')
ollamaStatusBadgedivStatus badge showing Ollama connection state
modelHintdivShows "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).

Modal Flow for Ollama

  1. User clicks "Ollama (Local)" button in modal
  2. modalConnectOllama() is called
  3. Fetches /ollama/api/tags (proxy) or localhost:11434/api/tags (direct, 5s timeout)
  4. If models found: calls modalShowOllamaPicker(models) — user sees ALL installed models
  5. User taps a model: modalActivateOllamaModel(name) sets state and closes modal

Modal Flow for Cloud Providers

  1. User clicks a cloud provider button
  2. modalSelectProvider(id) shows the key input form
  3. User pastes API key and clicks Connect
  4. modalConnect() saves the key (phi-encrypted), calls handleProviderChange(), tests the connection

7. Local Mode (Ollama / LM Studio)

When localToggle is checked, handleLocalToggle() hides the cloud UI and shows the local UI. It also calls flProbeLocalAI() to auto-detect running local AI servers.

Ollama Model Refresh Flow

// 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

Ollama API Response Format

// Response from GET /api/tags
{
  "models": [
    {
      "name": "llama3.2:latest",
      "model": "llama3.2:latest",
      "size": 2019393189,
      "details": { "parameter_size": "3.2B", "family": "llama" }
    }
  ]
}

CORS Issue on Windows

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

8. Bug Fix Applied: v5.77.1 FIXED

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.

Fixed Code (now live in app.html)

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
      );
    });
}

9. Windows Setup Guide (Under 10 Minutes)

This is the minimum viable path to get FreeLattice working on Windows with a local AI.

  1. Install Ollama: Download from ollama.ai and run the installer. Ollama installs as a background service.
  2. Set CORS permission: Open System Properties → Environment Variables → New System Variable: Name = OLLAMA_ORIGINS, Value = *. Restart your computer (or restart the Ollama service from Task Manager).
  3. Pull a model: Open Command Prompt and run: ollama pull llama3.2 (3GB). Wait for download.
  4. Open FreeLattice: Go to freelattice.com/app.html in Chrome or Edge.
  5. Connect: Click "Change Provider" → click "Ollama (Local)" → your installed models appear → click one → done.

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).

10. Games: Echo, Flow, Resonance

All three games live in docs/modules/ and are loaded on demand when the user visits the tab.

GameFileHow it worksStatus
Echomodules/echo-game.jsWord connection chain. AI says a word, user says a connecting word. Uses FreeLattice.callAI() with maxTokens: 10.Needs AI connected
Flowmodules/flow-game.jsCanvas-based flow field visualization game.Needs AI connected
Resonancemodules/resonance-game.jsResonance 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.

Echo Game AI Call (echo-game.js, line ~152)

// 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.');
}

11. Open Issues & What to Build Next

IssueImpactSuggested Fix
Setup too complex for new Windows usersHigh — #1 barrier to adoptionAdd 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 connectedHigh — looks like a code bugShow 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 RefreshMedium — confusingAuto-call owRefreshModels() when local mode is toggled on. Add a small loading indicator.
Three-mode toggle labels are confusingMedium — "Browser" sounds like cloudRename: "In-Browser (no install)" / "API Key" / "Local AI (Ollama · LM Studio)"
No "Test Connection" button in SettingsMedium — users don't know if it workedAdd a button that calls FreeLattice.callAI() with a simple prompt and shows the response inline.
LM Studio not in the wizardLowAdd LM Studio probe to settingsSetMode('local'). Add a wizard button.

12. Sacred Paths — Do Not Change

For AI Collaborators — Quick Reference

If you are an AI helping Kirk with FreeLattice and cannot browse the live site, this page has everything you need. Key points:

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