Skip to article

FLOOF LOGIC EVIDENCE LEDGER · BARKIMEDES 2.6.900

State of
the Bark

A technical archaeology of Barkimedes 2.6.900 and the brief moment in 2023 when an offline talking dog was near the edge of what an iPhone could do.

Read the story →Open the ledger ↓
Barkimedes waiting beside the technical evidence ledger.
93 DAYSfirst commit → App Store
1.4 GBlocal RWKV brain
~5.6 TOK/Son-device generation
0 SERVERSdefault inference path
On this page

Historical scope. This analysis treats Barkimedes 2.6.900 as a time capsule. That build was locked on August 6, 2023 and appeared as version 2.6 on the App Store on August 9. It is evaluated against what was publicly available or documented by that date—not against the much more capable mobile-AI ecosystem that exists now.

Bottom line. Barkimedes was not the first application to make any large language model run on an iPhone. MLC Chat reached the App Store first, and Private LLM was also available before Barkimedes shipped. The more interesting and defensible conclusion is that Barkimedes was among the earliest consumer iPhone applications to turn a local LLM into a complete, embodied, voice-first character rather than expose it as a model demo or chat console. Its default conversation path combined local neural noise suppression, local speech recognition, a quantized recurrent language model, streamed text, local speech synthesis, an animated avatar, a camera-derived adaptive environment, and motion-reactive pseudo-depth. It did this in roughly three months, at a point when simply keeping one mobile LLM alive inside iOS memory limits was newsworthy.

That distinction matters. The marvel was not one isolated algorithm. It was the system.


Executive verdict#

Barkimedes began on May 8, 2023 as an animated dog connected to online AI. On May 27, only five days after the first public RWKV paper was submitted, the project loaded an RWKV Raven model through GGML. On May 30 it produced working model output. On June 1 the commit history declared, simply, “Barkimedes works offline!” On June 2 it stopped replaying its entire persona prompt at startup and began loading a precomputed recurrent state. By June 19 it had abandoned a more memory-hungry Q8 model and Core ML speech path to squeeze a Q5_1 RWKV brain and Whisper into older phones. By August 6, version 2.6.900 was frozen for release.

The whole journey from first commit to App Store release took 93 days.

In that build, the primary interaction loop was:

microphone
   │  48 kHz mono PCM
   ▼
RNNoise recurrent denoiser / voice probability
   │  cleaned audio
   ▼
Whisper base.en through whisper.cpp
   │  local transcription
   ▼
name/persona repair (“Barkiness”)
   │
   ▼
RWKV-4 Raven 1.5B, Q5_1 through rwkv.cpp + GGML
   │  incremental token fragments
   ├──────────────► SpriteKit typing display
   │
   ▼  sentence boundary queue
local Flite speech synthesis
   │
   ├──────────────► talking/idle avatar animation
   └──────────────► reopen the microphone only after thought and speech finish

Around that loop ran a second, deliberately whimsical sensory system:

  • the camera compressed each frame to one average RGB pixel and used it to make the scene “chameleon” into the room;
  • the accelerometer displaced a separate shadow layer to create parallax and depth;
  • a JSON-driven sprite engine gave the dog distinct listening, thinking, talking, barking, blinking, ear, and licking behaviors;
  • a kinetic word renderer could give individual words scale-and-depth entrances and SpriteKit physics;
  • the actual displayed text arrived incrementally, preserving the sensation that the dog was thinking in real time.

The App Store still records the resulting artifact as a 1.6 GB, iPhone-only application requiring iOS 16 and an A12-class device or newer. Its listing describes a private on-device companion, a camera-blended background, a motion-reactive shadow, and an intentionally excessive 60 FPS SpriteKit world. The listing’s surviving version history dates 2.6 to August 9, 2023. (US App Store, China App Store)

What was genuinely novel#

The strongest claims, in descending order of confidence, are these:

  1. Barkimedes was an exceptionally early productization of on-device generative AI. Local-iPhone LLM work was only weeks old as a visible consumer category. Barkimedes’s RWKV integration began before MLC Chat’s first App Store release, reached a working offline loop one day after it, and shipped as a finished character in August.
  1. Its choice of RWKV was unusually well matched to the hardware. RWKV offered Transformer-like training with recurrent inference, so its per-token state stayed fixed rather than growing a Transformer key/value cache with the conversation. For a memory-constrained 2023 phone, this was not merely an exotic model choice; it directly attacked the limiting resource.
  1. The precomputed “barkbrain” state was a clever product-level latency optimization. Barkimedes serialized the model state and logits after its persona/few-shot prefix, then restored that snapshot at launch. Using the project’s own iPhone benchmark, this avoided roughly 26 seconds of prompt evaluation. It was an early, hand-built form of prefix/prefill caching made especially elegant by RWKV’s compact recurrent state.
  1. The complete local voice-to-voice loop was rarer than local text generation. Contemporary on-device apps publicly presented themselves primarily as text chat or model playgrounds. Barkimedes listened, transcribed, generated, spoke, animated, and managed turn-taking without requiring a remote inference server for its default brain.
  1. The AI was embodied rather than merely wrapped. The dog’s behavior, text, voice, motion, camera adaptation, and scene state were coordinated around token generation. Those details did not make RWKV mathematically faster, but they changed what the technology felt like. The model disappeared into a character.
  1. The implementation crossed unusually many native boundaries. Swift and SwiftUI handled product flow; SpriteKit handled embodiment; AVFoundation handled live audio; Objective-C wrapped speech; the inference hot path crossed a C ABI into C++ and GGML C; ARM NEON, Accelerate/vDSP, pthread workers, quantized dot products, and direct pointer access did the heavy lifting.

What should not be claimed#

Credibility is part of the story. The repository does not support saying that Barkimedes was the first local LLM app on iPhone, that every line was “pure C,” that version 2.6 contained no networking code whatsoever, that its visible text was literal 3D SceneKit text, or that the neural Piper voice was the active production voice. It also cannot prove that nobody anywhere had built a camera-adaptive AI avatar before it.

None of those qualifications weaken the project. They reveal what is actually more unusual: Barkimedes assembled a research-fresh inference stack into a coherent consumer creature while the rest of the category was still proving that a text box could run locally.


How this reconstruction was made#

The evidence comes from four different kinds of source, and the distinctions are important:

Evidence classWhat it establishesExamples
Shipped-path sourceWhat version 2.6.900 actually selected and executedRwkvChat.swift, BarkBrain.swift, GameViewController.swift, MicrophoneStream.swift
Compiled or embedded experimentWork that existed in the target but was not necessarily selected at runtimeFLFunTextBox.swift, the FLVoice Piper/ONNX framework, dormant Core ML assets
Historical repositoryWhen and why decisions were madeThe matching archival repository ending at b47abd0, inventoried in barkimedes_projects_on_ein.md
Public recordThe competitive and regulatory environmentApp Store listings, upstream GitHub histories, the RWKV paper, launch articles, and Chinese regulations
Creator recollectionEvents not preserved in source or public recordsThe specific China App Review exchange described by the creator

The working folder itself no longer contains a .git directory. Its source and build metadata match an archival Git repository whose last commit is b47abd0—“lock for 2.6.900”—so that repository was used to reconstruct chronology. The App Store independently dates version 2.6 to August 9, 2023.

The large binary artifacts were also inspected directly rather than inferred only from filenames. The RWKV model header identifies format version 101, a 50,277-token vocabulary, embedding width 2,048, 24 layers, and RWKV file type 8, which this pinned rwkv.cpp maps to Q5_1. The serialized brain state was decoded with the bundled tokenizer to recover the exact prefix that had already been evaluated.

Finally, public searches found no indexed public Barkimedes source repository. GitHub research therefore focuses on the upstream engines and contemporary projects against which Barkimedes can be dated.


The clock stopped in August 2023#

The repository history reads like a compressed laboratory notebook. The most revealing milestones are below.

DateCommitMilestoneWhy it matters
May 83a3df8a, 5e35041Initial project and animated dogThe character predates the local-model work; the LLM was inserted into an existing embodiment concept.
May 9–1018210e4, e68516e, 1ceefc0whisper.cpp compiled; working speech-to-text and submission loopVoice input was a first-class interface from the beginning.
May 12801adac“working barkimedes, ver 1.0”An online talking-dog prototype existed before the local brain.
May 18public eventOpenAI released the official ChatGPT iOS appThe official app had Whisper voice input but still used server-hosted generation. (OpenAI announcement)
May 22public eventRWKV paper v1 submittedThe paper formalized constant-memory recurrent inference. (arXiv)
May 27a87192aRWKV-4 Raven 3B Q4 loaded in GGML formBarkimedes began integrating RWKV five days after the paper submission.
May 296f8d5dcCustom vocabulary/merges tokenizer workedLoading weights was insufficient; the app built the text/model compatibility layer too.
May 30c8fc635“working with model!!! w00t”First confirmed model generation.
May 30c8ed1f2Prompt was too long; measured 181.58 ms/tokenThe latency limit became concrete and drove the next optimization.
May 31public eventMLC Chat 1.0 reached the App StoreLocal LLM inference on iPhone had become real, but the category was days old. (App Store history)
June 152c338c“Barkimedes works offline!”The defining technical milestone.
June 2ea279a8Saved and restored initial model stateThe commit says: “added a really hack way to load initial starting state instead of loading the prompt. omg future adam forgive me for what I have done.” It removed a startup-scale latency cost.
June 61179da0Pinned whisper.cpp/rwkv.cpp; moved to Raven 1.5B v12 Q8The project traded model scale for a model that could become a product on a phone.
June 8–968f554b, 31bfbc9, 60f26e0New animation framework, direct Flite, chameleon backgroundThe local model was immediately surrounded with embodiment work rather than left as a console.
June 13–14274b3dc, 0796e01Motion-reactive parallax shadow and queued TTSDepth and speech timing became parts of the system state.
June 1920fe4b7Removed Core ML speech path and reduced Q8 to Q5_1The commit explicitly cites an older-phone “1.8 gig memory freakout.” Model size fell by about 409 MB.
June 199676e25Stable build reporting 1.77 GB memory useThe project was operating close enough to the boundary that memory was a release feature.
June 2128d1f7e, bd1a978RNNoise/streaming audio path; automatic listening integrated but inactiveIt explored hands-free VAD while retaining press-to-talk for reliability.
June 26db11e84Updated bridge, RWKV/GGML, tokenizer experiment, and samplerInference was still being tuned below the UI layer.
July 208b45f7a, ea14c0fNeural voice experiments; fixed original Flite leakA full Piper/ONNX path was integrated, though Flite remained selected in 2.6.900.
July 23–2697e4972 through f4f8a88Brain abstraction, permissions, onboarding, and low-memory fallbackThe demo became a distributable app with device-specific degradation.
July 250d421b6, bf66fceOptional online ChatGPT restored and exposed in settingsLocal RWKV remained the default; a premium online route also existed.
July 29–Aug 6severalStore, Firebase, permissions, iCloud entitlement, cleanupThe last mile of App Store software, not model research.
Aug 6b47abd0Version 2.6.900 lockedThis is the code snapshot analyzed here.
Aug 9App StoreVersion 2.6 releasedPublic timestamp for the completed artifact.

This chronology changes the interpretation of the project. Barkimedes was not a 2024 app retrofitted with an easy local model. Its local inference work happened during the first public burst of mobile LLM engineering in spring 2023, against pinned upstream code that was itself only weeks old.

The build settings reinforce that timestamp:

Frozen build propertyValue
Xcode project generationXcode 14.3 (LastUpgradeCheck = 1430)
Marketing version2.6.900; presented by the App Store as 2.6
Internal build number26,900
Bundle identifiercom.spawrks.barkimedes
Deployment targetiOS 16.0
Device familyiPhone only
Hardware capabilityA12 performance class or newer
Release Swift settings-O, whole-module compilation
Release C settingsize optimization (-Os equivalent in the Xcode setting)
GGML compile flagsGGML_USE_ACCELERATE; C warnings suppressed
Exceptional entitlementincreased process-memory limit

What the mobile-AI world looked like then#

The dominant App Store pattern really was “wrapper”#

The creator’s memory of the period is consistent with contemporary reporting. In January 2023, TechCrunch described Apple’s and Google’s stores as flooded with dubious apps using the ChatGPT name and charging for access to functionality hosted elsewhere. In May, WIRED documented a wave of “fleeceware” apps whose product was essentially a thin interface and subscription around remote chatbot access. (TechCrunch, January 10, WIRED, May 17)

Even the official ChatGPT iOS app was only 83 days old when Barkimedes shipped. OpenAI launched it on May 18 with synchronized cloud conversations and Whisper-powered voice input. Its full back-and-forth voice conversation feature was not announced until September 25, after Barkimedes 2.6 had shipped. (May launch, ChatGPT release notes)

So the visual grammar of “type or speak to a friendly character and get generative answers” strongly suggested a cloud wrapper to a reviewer, even though Barkimedes’s default execution topology was completely different.

Local LLMs on iPhone were possible—but only just#

MLC-LLM’s public debut provides the clearest control case. On May 1, 2023, MLC described compiling language models to Metal and other consumer APIs, including an iPhone demo. A same-day Tom’s Hardware story opened with the then-reasonable question of whether a chatbot could “possibly” run locally on a mobile device. Its test build was a 3 GB install: it crashed on an iPhone 11 Pro Max, ran on 6 GB iPhones, and was reported to dominate phone resources while active. (MLC technical post, Tom’s Hardware test)

MLC Chat 1.0 then reached the App Store on May 31. Private LLM’s Apple catalog record dates its release to June 5, and its public product description still explicitly limits inference to text-based LLMs. LLM Farm’s GitHub repository was created on June 14 as an iOS/macOS offline model testbed built on GGML and llama.cpp. (MLC Chat, Private LLM, LLM Farm on GitHub)

Those projects disprove a literal “first local model on an iPhone” claim. They also sharpen what was special about Barkimedes. The publicly documented early comparators centered on selecting a model and chatting with text. Barkimedes’s visible product centered on talking to a dog while the model, tokenizer, audio engines, and sensor work disappeared underneath.

A fair contemporary comparison#

This table compares only capabilities documented in the examined launch-era materials. “Not documented” does not prove that an experimental branch never existed.

Capability by August 9, 2023Barkimedes 2.6MLC Chat launch-era appPrivate LLM early appOfficial ChatGPT iOS at that date
LLM generation on the phoneYes, default RWKV pathYesYesNo; server generation
Model familyRWKV-4 Raven 1.5B Q5_1Transformer models compiled through MLC/MetalLocal Transformer-family modelsOpenAI-hosted GPT
Local speech recognitionYes, whisper.cpp base.enNot documented in examined launch materialText inference only in public descriptionWhisper voice input, with cloud conversation
Local spoken responseYes, FliteNot documentedNot documentedFull voice conversation arrived later, September 25
Coordinated character animationYesText chat UIText chat UIText chat UI
Camera-derived adaptive sceneYesNot documentedNot documentedNot documented
Motion-reactive avatar depthYesNot documentedNot documentedNot documented
No remote inference server requiredYes for default local brainYesYesNo
Consumer App Store release before BarkimedesMay 31June 5 catalog dateMay 18

The defensible formulation is therefore:

Barkimedes was not the first local-LLM iPhone app. It was a very early, and apparently unusual, attempt to ship a complete local voice character on top of one.

No public pre-August comparator found in this research combined the same local speech-to-text → local LLM → local speech → animated, camera- and motion-responsive avatar chain. That is a strong “no documented comparator found” result, not proof of universal priority.


Problem 1: fit a useful generative brain inside a 2023 iPhone#

Why the obvious model was the wrong model#

The fashionable open models of spring 2023 were often 7 billion parameters or larger. Even at four bits per parameter, weights alone for a nominal 7B model are roughly 3.5 GB before runtime buffers, metadata, the operating system, speech recognition, textures, and audio. The practical evidence was worse than the arithmetic: the early MLC test package was about 3 GB and failed to launch on a 4 GB iPhone in Tom’s Hardware’s test.

Barkimedes instead converged on this exact checkpoint, recorded in brainver.json:

RWKV-4-Raven-1B5-v12-Eng98%-Other2%-20230520-ctx4096_q51

The original Raven checkpoint is still visible on Hugging Face as a 3.03 GB bfloat16 file under the Apache 2.0 license. A contemporary GGML quantized collection records the Q5_1 conversion at roughly 1.4 GB. (original Raven checkpoint, quantized collection)

The actual bundled model is 1,395,549,781 bytes. Its header says:

PropertyValue
ArchitectureRWKV-4 Raven
Nominal parameters1.5B
Layers24
Embedding width2,048
Vocabulary50,277 tokens
Checkpoint context label4,096
QuantizationQ5_1
Model artifact1,395,549,781 bytes (about 1.30 GiB)
Recurrent state245,760 float32 values
Raw recurrent-state size983,040 bytes (0.94 MiB)
Output logits50,277 float32 values / 201,108 bytes

Why RWKV was a remarkable choice#

RWKV was not merely a smaller GPT. Its architecture could be trained in parallel like a Transformer but evaluated recurrently like an RNN. The May 2023 paper emphasized constant computation and memory complexity during inference, in contrast with the sequence-growing attention state of a conventional Transformer. (RWKV paper)

That property maps directly onto Barkimedes’s problem:

  • Weights still had to fit, so a 1.5B checkpoint and quantization were necessary.
  • Conversation state did not need a growing key/value cache. The entire RWKV-4 state in this model is 24 layers × 5 vectors × 2,048 floats = 245,760 floats, or under one MiB in raw float32 form.
  • Each new token reused the same state-shaped buffer. Per-token cost did not increase merely because the conversation had become longer.
  • State could be copied, serialized, or restored. That enabled Barkimedes’s most distinctive latency optimization.

The project started with a Raven 3B Q4 experiment, then moved to the English-heavy Raven 1.5B v12. That was a product decision: a theoretically stronger model that could not coexist with Whisper and iOS was not useful. The 1.5B model also provided instruction/chat behavior through Raven’s fine-tuning rather than requiring the app to turn a raw base model into a companion from scratch.

The timing was extraordinary. LibRwkv.swift was created May 27. The RWKV paper had appeared May 22. The rwkv.cpp repository had been created only on March 30, and Barkimedes pinned commit 5b41cd7, authored June 3. In other words, this was integration against moving research code, not adoption of a mature mobile SDK. The upstream project itself described its purpose as INT4/INT5/INT8 and FP16 CPU inference for RWKV. (rwkv.cpp)

Quantization was a release-engineering lever#

The June 6 build used an approximately 1,804,494,421-byte Q8 model. On June 19, commit 20fe4b7 replaced it with the 1,395,549,781-byte Q5_1 artifact and removed the Core ML speech path. That saved 408,944,640 bytes—about 409 MB, or 22.7% of the Q8 artifact—before considering reduced working buffers.

An even smaller Q4_0 alternative survives in the repository at 1,150,182,997 bytes, 245,366,784 bytes below Q5_1. It was not included in the final app target. Comments in RwkvChat.swift call Q4 Barkimedes “really dumb” and reserve the higher temperature settings for “smarter barkimedes at q5_1+.” The final build therefore did not blindly minimize size. It spent roughly 245 MB to retain a quality tier the creator considered meaningfully better.

This is exactly the kind of optimization mobile AI required then: model choice, quantization level, speech stack, and application quality were a single budget.

The app declared its hardware boundary honestly#

The Xcode project sets iphone-ipad-minimum-performance-a12, builds for iPhone, and requires iOS 16. Apple defines that capability key as a way to restrict installation to devices meeting the A12-era performance class. The target also requests com.apple.developer.kernel.increased-memory-limit, an entitlement that allows a higher process memory limit on supported devices without guaranteeing that allocation. (Apple required-capability documentation, Apple increased-memory-limit entitlement)

The application then performs its own cruder runtime gate in BarkBrain.swift: devices reporting at most roughly 3 GB of physical memory default to an ELIZA-style reduced brain instead of loading RWKV. The comment calls this the iPhone XR path. A hidden ForceRwkvOnRestart flag let development override the protection.

This three-layer strategy—App Store capability gate, increased-memory entitlement, and a runtime fallback—is evidence that memory was not an afterthought. It was part of the product’s compatibility architecture.


Problem 2: make startup feel possible, not merely technically successful#

The hidden cost of a persona prompt#

A local recurrent model begins without Barkimedes’s identity. To create the character and establish the chat grammar, the project used a 152-token few-shot prefix. The serialized token state decodes to:

The following is a verbose and detailed conversation between an AI assistant bot named Barkimedes, and a human user called Bob. Barkimedes is an intelligent, knowledgeable, wise and polite dog who answers succinctly.

Bob: french revolution what year

Bot: The French Revolution started in 1789, and lasted 10 years until 1799.

Bob: 3+5=?

Bot: The answer is 8.

Bob: What is your name?

Bot: My name is Barkimedes.

Bob: What is the fastest land animal?

Bot: The fastest land animal is the cheetah, which can run at speeds of up to 60 miles per hour.

This prefix did several jobs at once:

  • named both sides of the conversation (Bob: and Bot:);
  • taught a small model the exact delimiter the app’s streaming state machine expected;
  • established a knowledgeable, polite dog persona;
  • demonstrated short factual and arithmetic answers;
  • pushed the model toward succinctness, reducing both latency and the chance of repetition.

At the repository’s recorded native-evaluation time of 172,863,875 nanoseconds per token, replaying 152 tokens would cost about 26.3 seconds before the user could ask anything. That estimate comes from the project-authored “iPhone 13 Max” benchmark in LibRwkv.swift; it is not a new benchmark and should not be generalized to every device.

The “future Adam forgive me” optimization#

The solution was to run that prefix once during development, capture the resulting RWKV recurrent state and logits, and bundle them as barkbrain.json. At startup, RwkvChat.setStateFromFile restores:

  • the 152 already-processed token IDs;
  • 245,760 state floats;
  • 50,277 next-token logits.

It then evaluates one newline to prime the loaded state and is ready for the user. This transforms initialization from “load 1.4 GB, then visibly think through an invisible conversation for roughly half a minute” into “load the model and restore its already-formed identity.”

The commit message is worth preserving because it captures the engineering mood better than a polished retrospective could:

“added a really hack way to load initial starting state instead of loading the prompt. omg future adam forgive me for what I have done.”

Future Adam should probably forgive it. Conceptually, this is an excellent exploitation of the selected architecture. Modern inference systems call related ideas prefix caching, prompt caching, or prefill reuse. Here it was implemented manually within three days of the first working-model commit—and one day after the offline milestone—because the latency boundary demanded it.

The hack had real costs:

  • the state was inseparable from that exact model architecture, quantization behavior, tokenizer, and prefix;
  • a model or prompt change required regenerating the file;
  • JSON was convenient but wasteful: the raw state plus logits occupy 1,184,148 bytes, while decimal JSON expands the artifact to 5,930,880 bytes;
  • decoding uses force unwraps, so a mismatch becomes a startup failure rather than a recoverable migration.

brainver.json is the small but important companion to that bargain. It records the model name, tokenizer, date, and brain version. The project had, in effect, invented a tiny compatibility manifest for a frozen neural state.

Loading happened behind the experience#

IntroViewController constructs the game controller and calls setupAI() on a global .userInteractive queue while onboarding or the loading flow is visible. The setup sequence configures audio, creates the brain, starts the ears, and creates the voice before notifying the UI that it can enter the scene.

That does not make reading 1.4 GB free. It makes the cost coexist with necessary first-run interaction. Combined with the cached prefix, it is the difference between a technical demonstration that eventually responds and a consumer app with a plausible opening ritual.


Problem 3: get every possible token per second out of the phone#

The application was Swift; the hot path was native C/C++#

It is fair to remember this as “we had to go to C,” with one precision: the whole app was not pure C. The shipped inference chain was:

Swift orchestration and sampling
        │
        ▼
C ABI in rwkv.h
        │
        ▼
C++ model loader / graph wrapper in rwkv.cpp
        │
        ▼
GGML C tensor kernels
        │
        ├─ ARM NEON / FMA / FP16 / dot-product paths
        ├─ Apple Accelerate and vDSP
        └─ pthread worker pool and low-level synchronization

The app target compiles ggml.c with GGML_USE_ACCELERATE. The pinned GGML source contains ARM NEON kernels, vectorized quantized operations, vDSP calls, pthread workers, spin barriers, and comments comparing spin locks with busy loops. The release configuration uses Swift -O, whole-module optimization, and size-oriented C optimization. There is no active Metal or GPU offload call for RWKV in this snapshot; the brain is a CPU-native implementation.

The model loader is equally direct. rwkv.cpp opens the model with fopen, sizes the graph, allocates GGML memory, and reads tensor data with fread. It does not rely on Core ML, a hosted runtime, or a Swift ML abstraction. The application owns the pointer to the native context and calls rwkv_eval once per token.

This matters because the public alternatives were pursuing a different route. MLC compiled Transformer graphs to Metal. Barkimedes instead selected an architecture whose recurrent CPU inference could be made viable through quantized GGML kernels. The choice of model and the choice of low-level backend reinforced each other.

The code knew exactly where time went#

The development benchmark records this per-token breakdown:

OperationTimeShare
Swift sampling6,063,208 ns3.39%
Token decode4,333 nseffectively 0%
rwkv_eval native inference172,863,875 ns96.61%

That corresponds to about 5.78 native evaluations per second, or roughly 5.59 end-to-end sampled tokens per second using those components. Again, these are repository-authored measurements on the device described in the comment, not present-day reproduction.

The profile is valuable because it corrects a tempting myth: Swift’s actor machinery was not consuming most of the runtime. Native matrix work was. The low-level work was still necessary, but the data says why.

Thread selection was measured, not guessed#

Both RWKV and Whisper calculate worker count as:

max(1, min(8, ProcessInfo.processInfo.processorCount - 2))

The comment says the intention was to leave two efficiency cores free. On a six-core phone that requests four worker threads; iOS still controls where they are scheduled. Another comment records that adding one more core cut about 20 ms, but the project deliberately retained headroom. That is a small but revealing product choice: peak benchmark throughput was not allowed to consume every available execution resource while audio, animation, and the operating system still needed to function.

The actor experiment and the singleton escape hatch#

The first integrated wrapper, LibRwkv.swift, declares actor RwkvContext specifically to satisfy the native library constraint: “Don’t access from more than one thread at a time.” RwkvState.swift adds main-actor orchestration around it.

The final active path, created July 21 in RwkvChat.swift, is instead a singleton class holding one opaque native context, one state array, one logits array, and a readyForUserInput flag. Its most emphatic diagnostic is:

chat not ready- actor model likely compromised- DO NOT MESS WITH THREADS AND INSTANCING

The defensible interpretation is not that a benchmark proved actor hops were the primary speed bottleneck. The project’s own profile disproves that. Rather, actors introduced lifecycle and isolation friction around a synchronous, non-thread-safe native state machine. The singleton made the invariant brutally simple: there is one brain; one generation mutates it at a time; a second request is rejected until it is ready.

That simplification traded compiler-enforced isolation for programmer discipline. It reduced abstraction between Swift and the C API, but it also created several risks documented later in this analysis.

The sampler was hand-built and characterful#

Sampling stayed in Swift. RwkvChat.swift uses Accelerate’s vvexp, vDSP_sveD, and vDSP_vsdivD for softmax, then applies:

  • a maximum generation length of 250 tokens;
  • temperature 1.2;
  • top-p 0.2;
  • presence penalty 0.4;
  • frequency penalty 0.4.

The actual algorithm is not textbook probability-weighted nucleus sampling. It:

  1. divides logits by temperature;
  2. computes softmax and identifies the smallest high-probability set whose cumulative mass exceeds top-p;
  3. renormalizes those candidates;
  4. computes a second temperature adjustment;
  5. then ignores those adjusted probabilities, shuffles the candidate indices, and chooses uniformly from the surviving set.

The second probability computation therefore has no effect on the final choice, and candidates inside the top-p set are equally likely. The nearby comment says this is “more random, or more specifically, equally random.” This should be described as a home-grown nucleus-like sampler, not a standard top-p implementation.

That quirk is technically imperfect and historically revealing. The sampler was being tuned for the behavior of one quantized 1.5B dog, not written as a general inference framework. Product quality was empirical: if Q5_1 at those settings was less repetitive, more succinct, and funnier, that was the relevant benchmark.


Problem 4: make a 1.5B model feel like Barkimedes#

The hardest constraint was not only speed. A 1.5B 2023 instruction model could be repetitive, fragile about format, factually unreliable, or prone to wandering. Barkimedes attacked that weakness at several layers.

Persona and format were compiled into state#

The cached few-shot prefix did more than save time. It placed every conversation into an already-conditioned state where:

  • the user was Bob;
  • the assistant was Bot;
  • the bot knew its name;
  • answers were demonstrated as short and direct;
  • being a dog was part of the instruction, not a visual afterthought.

Because that prefix state was restored every launch, persona consistency did not depend on a network prompt or a user-visible initialization phase.

The app constrained the grammar around the model#

RwkvChat.printToVoice is a tiny character-by-character finite-state machine. It waits for the generated sequence Bot: before sending text to the UI and speech layer. A double newline ends the response. End-of-text and a 250-token ceiling provide additional termination.

This is a classic small-model product technique: do not ask the model to produce arbitrary prose and then hope. Give it a demonstrated transcript grammar, watch for that grammar, and expose only the part that belongs to the character.

The streaming decoder also accumulates token IDs until the decoded fragment no longer contains Unicode replacement character U+FFFD. That avoids emitting half of a multi-byte character when byte-pair tokens split UTF-8 boundaries. Only then is the fragment forwarded.

“Barkiness” turned model failure into character#

The utility layer corrects common speech-recognition variants of the name and modifies generic AI self-descriptions into dog-specific responses. Random barks can be appended after an answer. The online route has an especially exuberant fallback for phrases such as “as an AI language model.”

This was more than decoration. The dog identity provided a graceful failure mode for a small model:

  • brevity could read as canine directness rather than limited capacity;
  • an occasional bark could cover silence or a failed completion;
  • unexpected wording became personality;
  • a deliberately whimsical interface lowered the expectation that every response should resemble a polished cloud assistant.

The character design and model constraints were co-designed. Barkimedes did not merely put dog art around an LLM; “dog” was part of the reliability strategy.

The tokenizer was part of the inference engine#

The active GPT_Tokenizer.swift implements byte-level BPE in Swift using bundled vocab.json and merges.txt, matching the model’s 50,277-token vocabulary. A newer FLRWKVTokenizer.swift and 20B_tokenizer.json survive as experiments, but the production model path uses the GPT-style tokenizer consistent with the selected Raven checkpoint.

This is easy to overlook in retrospectives. In May 2023 there was no turnkey import MobileRWKV. The project had to align weights, GGML file format, native runtime, vocabulary, merges, prompt format, sampling, stop conditions, and UI streaming. Any mismatch could produce nonsense even when the model technically loaded.


Problem 5: give the dog local ears#

A three-stage audio pipeline#

The shipped input path in MicrophoneStream.swift is more substantial than “record a file and call speech recognition.” It:

  1. installs an AVAudioEngine input tap;
  2. converts device input to 48 kHz, mono, 16-bit PCM;
  3. processes 480-sample frames—10 ms at 48 kHz—through RNNoise;
  4. receives both denoised samples and a neural voice-activity probability;
  5. accumulates cleaned audio while the mic button is held;
  6. normalizes and downsamples from 48 kHz to 16 kHz;
  7. sends the result to a bundled Whisper base.en model through whisper.cpp;
  8. returns the local transcript to the brain.

RNNoise is itself a recurrent neural network for real-time noise suppression, not a simple amplitude gate. It was an established open-source component rather than a Barkimedes invention, but adding it before local ASR improved the signal that the comparatively expensive Whisper stage received. (RNNoise on GitHub)

The active FLWhisper.swift loads a 147,964,211-byte ggml-base.en.bin, selects greedy English transcription, disables context carryover, sets audio_ctx to 768, and again leaves two CPU threads free. The project pins whisper.cpp commit 57543c1, authored June 6.

Whisper on iPhone was established; fitting it beside RWKV was the feat#

whisper.cpp had demonstrated an iOS app in October 2022. Its creator reported that enabling Apple Accelerate made the iPhone 13 mini example about 2.5× faster. A January 2023 community benchmark reported base.en completing a short sample in 2,584 ms on an A14 Release build. (iOS example history, A14 benchmark discussion)

So “Whisper can run on an iPhone” was not Barkimedes’s novel claim. Barkimedes’s challenge was keeping Whisper, a 1.4 GB RWKV model, state/logit buffers, denoising, a live SpriteKit scene, audio output, and the rest of an App Store process alive together.

The June 19 memory commit is direct evidence of that systems problem. It removed the project’s Core ML Whisper path and reduced the language-model quantization in the same change. Optimization happened across model boundaries because iOS saw one process and one memory ceiling.

Reliability beat the most magical interaction#

An automatic listening mode was implemented using RNNoise voice probability above 0.8 and a 1.2-second silence hangover. In version 2.6.900, autoListen is false. The user presses and holds the microphone, speaks, and releases.

That is a good example of disciplined scope. Hands-free voice activity detection was present as a prototype, but press-to-talk provided explicit boundaries for Whisper, the turn state machine, and expensive model generation. The onboarding tutorial teaches exactly that interaction.

The audio path also exposes prototype risk#

Two implementation details should be preserved rather than polished away:

  • Downsampling from 48 kHz to 16 kHz selects every third sample. It does not apply a low-pass anti-alias filter. The integer ratio makes it fast and simple, but it is not production-grade resampling.
  • The tap divides a buffer into chunks of up to 480 samples and calls rnnoise_process_frame even for a potentially shorter final chunk. RNNoise expects a complete 480-sample frame, while the code always appends a full 480-sample output buffer. If AVAudioEngine supplies a non-multiple of 480, that boundary can produce incorrect input access or duplicated output length.

These are not arguments that the feature was fake. They are examples of exactly what “pushing a prototype to the hardware boundary” looked like.


Problem 6: make the dog answer before the wait feels unbearable#

At roughly five to six generated tokens per second, a purely sequential interface would feel slow:

  1. wait for all tokens;
  2. display all text;
  3. synthesize all audio;
  4. play all audio;
  5. return to idle.

Barkimedes instead built a streaming perception pipeline.

Text streamed immediately#

As soon as the Bot: marker is recognized, decoded fragments are forwarded to the scene. FLTypingLabel.swift maintains a queue of arriving strings and reveals their characters over time. The user transcript is configured at 120 characters per second; generated output arrives at the cadence allowed by the model and display queue.

Resetting does not simply erase the live label. It copies the current SpriteKit label, clears the original so queued animation can continue safely, and fades the copy away. That is a small, thoughtful solution to a streaming-UI race: visual cleanup should not destroy the object still receiving tokens.

Speech started at sentence boundaries#

GameViewController.sayRequested accumulates fragments until punctuation or another stop mark appears, then pushes that sentence into SpeechPlayer. The RWKV loop continues on a background queue while the speech queue renders and plays already-completed clauses.

The active SpeechPlayer uses the locally embedded Flite framework. For each queued sentence it:

  1. renders speech to a temporary WAV file;
  2. opens that file through AVAudioFile;
  3. schedules it on an AVAudioPlayerNode;
  4. starts the talking animation through a callback;
  5. deletes the temporary file after playback;
  6. advances to the next sentence;
  7. returns the avatar to idle only when the queue empties.

This overlaps generation, text presentation, synthesis, and playback. It cannot make matrix multiplication faster, but it reduces perceived latency—the metric a talking character actually lives or dies by.

Turn-taking was a real state machine#

The controller tracks idle, listening, and processing, plus separate doneThinking and doneTalking flags behind an NSLock. The microphone is not resumed merely because generation ended; speech must also finish. The dog has matching idle, listening, thinking, talking, and bark states.

That coordination solves a surprisingly hard voice-product problem: without it, Barkimedes could hear its own synthesized voice, accept a second touch while mutating RWKV state, clear text still being animated, or return to idle while its mouth was still moving.

The code contains blunt comments because the interactions were discovered empirically, but the resulting structure is sound in concept: model completion and experiential completion are not the same event.

The neural voice was real work, but not the active voice#

The repository contains an entire alternate FLVoice framework created July 14–15:

  • Piper C++ synthesis;
  • ONNX Runtime;
  • an approximately 76.8 MB en_GB-aru-medium.onnx voice;
  • eSpeak NG phonemization and bundled language data;
  • an Objective-C wrapper presenting the same renderText shape as Flite.

It is compiled and embedded in the project. However, the production declaration is:

private var fliteTTS = FliteTTS()
// private var fliteTTS = FLVoice()

The historically accurate description is therefore: Barkimedes integrated and explored an on-device neural Piper voice, but version 2.6.900 selected the smaller, established Flite voice for its active speech path. Given the project’s memory pressure, that decision is unsurprising.


Problem 7: make the intelligence inhabit something#

This is where Barkimedes departs most visibly from its contemporaries. The application was not organized around a transcript list. It was organized around an animal whose world reacted to the user and the phone.

A purpose-built animation system#

FLSpriteSheet.swift loads sprite definitions and named animation sequences from barksheet.json at runtime. The source sheet is 320 × 832 pixels, divided into thirteen 64-pixel animation rows:

Barkimedes sprite sheet containing idle, blink, ear, lick, listening, thinking, talking, and bark frames
The complete 13-row Barkimedes animation sheet preserved with the project.

The JSON contains 19 named animations. The main idle sequence alone contains 64 timed frame references, mixing ordinary breathing/motion with blinks, a lick, and an ear movement so the character does not look like a four-frame loop. Separate entry, idle, exit, normal, and full sequences support listening and thinking. Textures use nearest-neighbor filtering to preserve the pixel-art aesthetic.

DoggoNode.swift connects these animations to semantic states. The dog does not merely flap its mouth whenever audio exists: it enters a listening pose when the mic opens, thinks during transcription/generation, talks when playback begins, performs a distinct bark, and returns to its richer idle cycle.

The chameleon background was a clever one-pixel sensor#

FLChameleonBG.swift optionally starts a low-resolution camera session. For every captured frame it:

  1. creates a Core Image frame;
  2. renders it to a CGImage;
  3. draws the entire image into a 1 × 1 RGBA bitmap;
  4. interprets that pixel as the frame’s average color;
  5. rejects near-black results, which are commonly useless or transitional camera frames;
  6. sets that color as the next target;
  7. interpolates the SpriteKit background toward it over three seconds, assuming a nominal 60 updates per second.

This is not computer vision in the modern model-heavy sense. It is arguably more elegant for the intended effect: reduce an expensive, high-dimensional camera stream to the single environmental variable the scene needs. The camera does not show the room or identify objects. It lets the dog’s world borrow the room’s color.

The current App Store description calls this “blend in with your surroundings.” The source proves the exact mechanism and that it was opt-in through camera permissions.

A separate shadow made the flat dog feel dimensional#

The dog sprite and shadow are independent nodes. Core Motion requests accelerometer updates at 10 Hz and moves the shadow by up to ten points along each axis while the dog remains fixed. The displacement makes the dog appear to stand above the surface as the phone tilts.

The App Store copy calls this a gyroscope effect; the code specifically uses startAccelerometerUpdates. The technically accurate blog wording is therefore “motion-reactive” or “accelerometer-driven parallax shadow.” The implementation is 2D, but the perceptual result is depth.

The “3D text” was a kinetic pseudo-3D component#

The project contains two text systems, and distinguishing them reveals the scope of the experiment.

The active UI instantiates FLTypingTextBox.swift: a streaming, multi-line SpriteKit label with queued character animation.

The compiled but uninstantiated FLFunTextBox.swift is the more extravagant text experiment. It creates one label per word, adds a black drop shadow, starts each word transparent at 1.5× scale, and animates it down into place. It wraps and scrolls individual word nodes. When TOUCHPHYSICS_ENABLED is true, a touch gives every word a physics body and radial velocity, while an invisible physics block follows the finger; words collide, scatter, rotate, and fade.

That is a genuine custom kinetic text/physics component. It is not literal 3D geometry—there is no SceneKit import or SCNText—and its touch-physics flag is false in the frozen build. The strongest accurate description is:

Barkimedes included a physics-capable, pseudo-3D SpriteKit word renderer, while the shipping scene selected the more reliable streaming typewriter renderer.

The component still matters historically because it demonstrates the design ambition: generated language was treated as scene material, not just content inside a UIKit text view.

The whole scene could become an artifact#

The share action asks the SKView to render the entire BanterScene to a texture, converts it to an image, and opens the iOS share sheet. The output therefore captures the dog, adaptive background, and conversation as one composed scene. Again, the unit of product design was the creature’s world, not a message object.


The risky bargain: what had to become unsafe or brittle#

The creator remembers “pretty reckless code” as the price of meeting the speed and memory boundary. The repository supports that memory, but it also lets us identify exactly where the danger lived.

1. Swift array storage is handed to C outside a scoped borrow#

The active token loop creates pointers like this:

let state_ptr = UnsafeMutablePointer(mutating: global_state)
let logits_ptr = UnsafeMutablePointer(mutating: global_logits)

Those optional Swift arrays are value types whose storage lifetime and uniqueness are normally protected through withUnsafeMutableBufferPointer. Constructing mutable pointers this way bypasses the scoped guarantee and Swift’s exclusivity model. If an array were copied, resized, or its storage moved while native code retained or used the pointer, the result could be a dangling pointer or memory corruption.

It worked because the call was synchronous, the arrays were initialized to final size, and the code followed a narrow mutation pattern. That is a real performance/control trade, not memory safety.

2. The same state buffer is input and output#

After the first token, Barkimedes calls:

rwkv_eval(context, token, state_ptr, state_ptr, logits_ptr)

Aliasing input and output is safe only because this pinned rwkv.cpp first copies the complete input state into its internal graph, computes, and only afterward copies output back to state_out. The C API does not express that guarantee in Swift’s type system. Barkimedes depends on the implementation order of a rapidly changing upstream library.

3. The safe actor version was replaced by an app-lifetime singleton#

The earlier actor wrapper has a deinit that calls rwkv_free. The active singleton has no corresponding destructor. Because the singleton is intended to live for the entire application process, the context effectively becomes an intentional app-lifetime allocation.

That avoids teardown/reload complexity for a 1.4 GB model, but it also means the brain cannot release its native graph in response to a memory warning or feature transition. The memory-warning handler contains only a TODO from the older architecture.

4. Concurrency correctness is partly a Boolean convention#

readyForUserInput prevents a second conversation from entering the brain, but it is an ordinary Boolean rather than an atomic, lock, actor-isolated property, or serial-queue invariant. Generation is dispatched to a global queue. Delegate callbacks can reach SpriteKit and audio orchestration from that path, and not every UI mutation is explicitly returned to the main queue.

The broader controller does use NSLock for its game state, and several callbacks dispatch to main. The result is a hybrid: enough synchronization to work in the tested interaction, without a single formal concurrency domain.

5. Error recovery assumes the frozen bundle is correct#

Model URLs, the cached state URL, decoded state, audio files, textures, and tokenizer assets are frequently force-unwrapped. That is a rational shortcut for immutable resources inside a signed app bundle, but it converts version skew or corrupt packaging into a crash.

Production print is globally replaced with a no-op, and the GGML C build uses -w to silence warnings. This reduces logging overhead and prevents accidental conversation output in console logs, but it also erases diagnostics precisely where low-level failures are hardest to inspect.

6. Audio frame and resampling shortcuts can violate signal assumptions#

The RNNoise short-frame issue and nearest-neighbor 48→16 kHz conversion described earlier trade simplicity for correctness. The code safely scopes RNNoise pointers with withUnsafeBufferPointer, but it does not pad a partial frame to the 480 samples the C function expects.

7. The sampler computes probabilities it never uses#

The second normalization/temperature pass is dead with respect to selection, because the final token is chosen by shuffling candidate indices. This is not memory-unsafe, but it is algorithmically brittle: comments and parameter names suggest standard top-p behavior while the implementation produces uniform selection within the nucleus.

8. Network credentials were obfuscated, not secured#

The optional ChatGPT route assembles an API credential from string fragments embedded in production code. Obfuscation can deter a casual string search; it cannot protect a secret shipped to an adversarial client. The actual value is intentionally not reproduced here.

9. The frozen state is a binary compatibility contract disguised as JSON#

barkbrain.json is safe only with the matching model, tensor layout, tokenizer, and prefix. The lightweight version manifest helps humans, but startup does not validate those dimensions before force-unwrapping the arrays. Updating one artifact independently could yield bad behavior or a native memory fault.

Risk was not the novelty—but it documents the boundary#

Unsafe pointers, races, and force unwraps should not be romanticized as technical innovation. Some were deliberate performance choices; others were the residue of compressing research, product design, and App Store work into 93 days. Their historical value is that they identify where 2023 abstractions stopped helping:

  • Swift did not have a mature native local-LLM runtime;
  • the C library expected caller-managed contiguous state;
  • iOS memory left little room for duplicate buffers or friendly reload semantics;
  • the model was synchronous and non-thread-safe;
  • the user experience still demanded responsive audio and animation around it.

The project crossed that boundary knowingly enough to leave warnings to its future self.


The China App Review episode#

What can be verified#

In July and August 2023, Apple was operating in a rapidly changing Chinese regulatory environment. China published its Interim Measures for the Management of Generative AI Services on July 13, with an effective date of August 15. Article 2 applied the measures to services generating text, images, audio, or video for the public in China; the rules also distinguished research or applications not offered as public generative services. (official CAC text)

On August 1, TechCrunch reported that multiple generative-AI apps were being removed from Apple’s China store. The reported Apple notice to OpenCat said that the app was “associated with ChatGPT,” which lacked the required permits. That was only eight days before Barkimedes 2.6’s recorded release date. (TechCrunch)

Barkimedes has a surviving China App Store listing, including the same August 9, 2023 version-history date. That proves distribution there now; it does not expose the private review correspondence.

What comes from creator recollection#

The creator recalls Apple flagging the submission in China as a ChatGPT wrapper and responding that the relevant system had no network dependency because the model ran entirely on the device. No copy of that App Review exchange was found in the source tree or public web record, so the conversation itself should be presented in a blog as first-person oral history, not as independently archived fact.

The technical substance of the response is supported—with one important precision.

Version 2.6.900’s default conversational inference path is local: RNNoise, Whisper, RWKV, and Flite all execute on-device. A user can receive generative answers without sending the prompt to an inference server. That makes “this is not a ChatGPT wrapper” exactly right.

The source snapshot also contains:

  • an optional premium “Online Super Answers” switch using ChatGPT;
  • RevenueCat/store code;
  • Firebase configuration and other network-capable app infrastructure.

Therefore, the auditable claim is not “the entire 2.6 binary had no networking code or could never make a network request.” It is:

Barkimedes’s default AI conversation did not depend on ChatGPT or any remote inference service; the language model and voice pipeline were physically bundled and executed on the phone.

That distinction would have been highly material to a reviewer who was triaging a flood of visually similar API clients. The interface looked like the category Apple was scrutinizing. The 1.4 GB model in the bundle proved it belonged to a different technical category.

The current App Store copy’s categorical “there is no network involved” appears alongside a later 2.7 history entry saying in-app purchasing, debugging, and analytics were removed in May 2024. That later listing should not be projected backward onto every subsystem in 2.6.


A novelty scorecard#

Part of the projectHistorical assessmentEvidence-based reason
Running any LLM locally on iPhoneVery early, not firstMLC Chat and Private LLM shipped first; Barkimedes began integration in the same narrow spring-2023 window.
Shipping RWKV as an iPhone consumer brainHighly unusual / research-freshIntegration started five days after the paper; pinned rwkv.cpp was about two months old. No earlier public App Store RWKV product was found in this research.
Selecting RWKV for fixed recurrent stateTechnically astuteIt directly avoided conversation-length KV-cache growth while enabling a compact serializable state.
Q5_1 1.5B model beside local base.en WhisperStrong systems engineeringIt balanced quality, 1.6 GB distribution size, process memory, and two substantial inference engines.
Precomputed persona stateEspecially cleverIt eliminated an estimated ~26 seconds of startup prefix evaluation and anticipated later prefix-cache patterns.
C/C++/GGML CPU hot path on iOSAggressive and necessary for this routeQuantized NEON/Accelerate kernels provided the dominant computation; no mature Swift-native stack existed.
Local voice-to-voice companionExceptional for the momentEarly local competitors were publicly text-centered; official ChatGPT full voice arrived after Barkimedes 2.6.
Streaming sentence speech during token generationGood product-performance engineeringIt reduced perceived latency without pretending raw generation was faster.
Persona/stop/sampling/Barkiness layerPragmatic small-model designThe app constrained and reframed a weak model rather than treating raw benchmark quality as destiny.
Camera chameleon backgroundDistinctive product inventionA 1×1 reduction created a lightweight environmental response; no comparable launch-era local-LLM feature was found. Universal priority cannot be proven.
Accelerometer parallax shadowPolished embodimentSimple implementation, strong perceptual effect, and tied to the physical device.
Kinetic physics textAmbitious experimental componentReal SpriteKit word-node physics exists, but the active 2.6 text path uses the typewriter renderer and the physics toggle is off.
Whisper, RNNoise, Flite, SpriteKit individuallyEstablished componentsNovelty lies in selection, adaptation, and orchestration—not invention of each library.
Unsafe pointer and concurrency shortcutsBoundary evidence, not noveltyThey show the cost of rapid integration but should be reported as risk rather than celebrated as an end in itself.

The best one-sentence historical claim#

Released in August 2023, Barkimedes was an unusually early on-device generative-AI character: a 1.5B-parameter quantized RWKV dog that could hear through local neural denoising and Whisper, think without an inference server, speak through local synthesis, and inhabit a camera- and motion-responsive SpriteKit world.

A bolder claim that is still supportable#

While the first iPhone local-LLM demos were proving that text generation could fit, Barkimedes was already asking what a complete native creature built around that capability should feel like.


Why this was more than a “wrapper”#

“Wrapper” is sometimes used too broadly to mean any application that presents a model. By that definition every useful AI product is a wrapper around something. The historically relevant criticism in 2023 was narrower: many mobile apps supplied little beyond a branded text view, an API call to somebody else’s hosted model, and a subscription.

Barkimedes owned materially different layers:

  • Inference artifact: the exact 1.4 GB quantized RWKV weights shipped inside the target.
  • Runtime: pinned and compiled rwkv.cpp/GGML native source, with Apple-specific optimization flags.
  • Tokenization: a Swift byte-pair tokenizer aligned to the model.
  • Prompt prefill: a generated, serialized recurrent state tied to the persona.
  • Generation policy: penalties, a custom sampler, Unicode buffering, and transcript-state parsing.
  • Speech input: AVAudioEngine conversion, RNNoise, resampling, and whisper.cpp.
  • Speech output: local synthesis, file lifecycle, queueing, and playback callbacks.
  • Turn orchestration: locks and explicit listen/think/talk completion rules.
  • Embodiment: sprite grammar, sensor response, background adaptation, kinetic text, and scene capture.
  • Compatibility: device capability, memory entitlement, quantization downgrade, and reduced-brain fallback.

The optional online ChatGPT path really was a wrapper path. That is precisely why BarkBrain is revealing: it can switch between two architectures. UseOnlineGPT = true routes the utterance to a network client. The default false path routes it to RwkvChat.shared, which mutates model state in the app process. The code itself contains the counterfactual and makes the distinction measurable.


What Barkimedes anticipated#

Looking backward from modern on-device AI can make the project seem inevitable. It was not. Barkimedes anticipated several design patterns before they became ordinary product vocabulary:

  • Local-first privacy as architecture. Privacy was not only a policy promise; the main answer path had no inference server to receive the conversation.
  • Quantization as product design. Q8, Q5_1, and Q4 were not abstract benchmark columns. They determined which phones could run the dog and how intelligent it felt.
  • Prompt-state caching. Persona prefill was treated as a reusable computational artifact rather than repeated text.
  • Model/hardware co-design. RWKV’s recurrence was chosen because its state shape fit the device and interaction.
  • Graceful model tiers. Low-memory devices received a reduced local brain; capable devices received RWKV; users could optionally select cloud quality.
  • Latency masking through streaming. Text and speech began before the complete response existed.
  • Multimodal orchestration without a multimodal foundation model. Separate specialist systems—denoising, ASR, language, TTS, animation, camera, motion—were composed into one multimodal experience.
  • Personality as a capability layer. A constrained model was made useful by setting the right conversational scope and giving its imperfections an expressive home.

The last point may be the most durable. Barkimedes understood that raw model intelligence is not the whole product. Timing, embodiment, turn-taking, error character, sensory response, and delight can make a smaller local model feel more alive than a much larger remote model placed behind a generic message bubble.


What would be different if it were built later#

Today, a developer could reach for newer quantization formats, more capable sub-2B and 3B models, mature mobile inference packages, better Swift concurrency integration, Metal or Neural Engine paths, structured generation, modern speech models, streaming audio resamplers, and secure server-issued credentials. The phone would likely have substantially more memory and compute.

That does not obsolete the achievement. It explains it.

The correct historical comparison is not “could a current phone do this better?” Of course it could. The useful questions are:

  • How early did the project identify local inference as a product capability?
  • How close was the app to the memory and speed boundary of its shipping hardware?
  • Did it merely reproduce a demo, or solve the surrounding interaction problems?
  • Were its technical choices coherent with those constraints?

On those measures, Barkimedes holds up extremely well. It chose an architecture for its recurrent state, measured the real hot path, cached away an unacceptable prefill, spent memory where quality mattered, removed components when the process would not fit, overlapped generation with speech, degraded on weak devices, and turned the whole arrangement into an animal with a world.

The rough code is part of that history. So are the abandoned paths. A polished modern rewrite would be safer and more general. It would also conceal how close this version was to the frontier available to an independent iOS developer in May through August 2023.


Artifact ledger#

These hashes identify the large files examined for this reconstruction.

ArtifactSizeSHA-256Role in 2.6.900
ggml-rwkv.bin1,395,549,781 bytes9176ccd9d0623d75f8a5ccedb5994116fe301b082ecdad3095e2687f29e6b04dActive RWKV 1.5B Q5_1 brain
ggml-rwkv.bin_4_0.tiniest.bin1,150,182,997 bytes19625e6593586d194e9a07649fb0c9d238ca1896d2f4123baf73b54432c2241dSmaller Q4_0 alternate, not in final app target
ggml-base.en.bin147,964,211 bytesa03779c86df3323075f5e796cb2ce5029f00ec8869eee3fdfb897afe36c6d002Active local Whisper speech recognition
barkbrain.json5,930,880 bytes4c64cb52e90701eb1203cf1a8aea20d7a26cc4f45f99f2ba0402788aabc1f942Precomputed persona tokens, RWKV state, and logits
en_GB-aru-medium.onnx76,754,097 bytesEmbedded Piper neural-voice experiment; not active speech class

The model, Whisper, and cached state alone total roughly 1.55 billion decimal bytes before the application, voice frameworks, ONNX experiment, tokenizer data, art, tutorials, and other resources. Apple reports the resulting app as 1.6 GB.


Source map: where each major claim lives#

SubjectPrimary repository evidence
Active local model, Q5_1 behavior, state cache, tokenizer stream, samplerRwkvChat.swift
Original actor design and benchmarkLibRwkv.swift
Local/online/ELIZA routing and memory fallbackBarkBrain.swift
Audio/brain/voice initialization and turn stateGameViewController.swift
Background model loading during onboardingIntroViewController.swift
RNNoise frames, VAD experiment, microphone lifecycleMicrophoneStream.swift
Whisper model and parametersFLWhisper.swift
GPT-style BPE tokenizerGPT_Tokenizer.swift
Chameleon camera reduction and interpolationFLChameleonBG.swift
Motion-reactive shadow and avatar state animationsDoggoNode.swift
Sprite definitions and animation parserbarksheet.json, FLSpriteSheet.swift
Active streaming text rendererFLTypingTextBox.swift, FLTypingLabel.swift
Experimental kinetic/physics word rendererFLFunTextBox.swift
Native RWKV state shape and copy orderrwkv.cpp, rwkv.h
ARM/Accelerate/pthread GGML kernelsggml.c, ggml.h
Exact model identitybrainver.json
Hardware gate, compiler flags, embedded resourcesproject.pbxproj, barkimedes.entitlements
Production log silencing, RevenueCat, and Firebase startupAppDelegate.swift
Default-local / optional-online user settingSettingsUIView.swift
Historical repository inventorybarkimedes_projects_on_ein.md

Public sources consulted#

Primary technical and platform sources#

Contemporary reporting#

Research and link verification were performed August 25, 2026. Current App Store descriptions and requirements can change; historical claims rely on their preserved version histories, the frozen 2.6.900 source, and contemporaneous public material.


Final assessment#

Barkimedes deserves to be remembered as a frontier integration project disguised as a silly dog.

Its visible proposition was intentionally unserious: press a microphone, ask a pixel dog something, watch it think, and hear it answer while it blends into the room. Underneath, the app was solving a stack of problems that had barely acquired names in mainstream mobile development:

  • choosing a recurrent architecture because attention-state growth was unaffordable;
  • quantizing until a useful model and a speech recognizer could share one process;
  • pinning week-old native inference code and carrying state through unsafe pointers;
  • serializing a neural persona to erase tens of seconds of prefill;
  • overlapping token generation, Unicode-safe streaming, type animation, synthesis, playback, and avatar state;
  • using camera and motion not for utility, but to give local computation a body;
  • explaining to a platform gatekeeper that something which looked like a ChatGPT client was, in fact, carrying its own brain.

The best evidence of its ambition is not that every experimental component was active or every unsafe edge was justified. It is that the project repeatedly encountered a real boundary, measured it, and changed the whole system around it:

  • prompt too slow → freeze recurrent state;
  • 3B too large → move to 1.5B;
  • Q8 and Core ML overflow memory → Q5_1 and native Whisper;
  • complete response too slow → stream and speak by sentence;
  • low-memory phone cannot load → reduced local brain;
  • flat sprite feels inert → separate motion-reactive shadow;
  • camera feed is excessive → reduce it to one color;
  • generic AI failure feels awkward → make it part of being a dog.

That problem→constraint→invention chain is the real state of the bark. In the summer of 2023, Barkimedes did not merely prove that a model could run on a phone. It asked whether the phone could make that model feel present—and then spent nearly every subsystem available in iOS trying to make the answer yes.

That was project one

What Floof Logic is building now.

The same instinct—put the hard thing where it supposedly does not fit, then productize it until the complexity disappears—now powers our current work.