Field notes · M4 mini · 32 GB

What actually runs on a 32 GB Mac mini — June 2026

Six months of running local LLMs, image generators, and a music-analysis model on the same Apple Silicon box. Decode rates, prefill costs, quality scores, and a stack of dead ends. Everything below is measured — the scripts and raw JSON live in the repo.

June 2026 snapshot · Mac mini M4 · 32 GB unified · ~120 GB/s bandwidth
00

Executive summary — what we kept

6 slots

After cycling through roughly twenty open-weight models across LLM, image, and music tasks, three slots are settled and a fourth — a general-purpose local audio LLM — is still vacant. The shortlist below is what survived the cuts. Dates are upstream release months — quant uploads and Ollama-MLX repackagings can lag a few weeks behind.

SlotWinnerReleasedWhy it beat the field
Tool-calling agent LLM Qwen3.6-35B-A3B-coding NVFP4 MLX Apr 2026 34 tok/s on Ollama-MLX, 41 tok/s on rapid-mlx; 7/7 on the tool-call suite. MoE with 3 B active = bandwidth-class of a 3 B model, capacity of a 35 B.
Chat · vision · long-doc LLM Gemma 4 26B-A4B QAT NVFP4 Jun 2026 34 tok/s decode (ties Qwen), wins vision (1.6× decode and 4.3× fewer image tokens vs Qwen3-VL-8B), wins long-doc decode (2.3× over Gemma 4 12B at 46 K context).
Image generation — speed FLUX.2 Klein 4B via MFLUX Jan 2026 60 s per 1024² image at 4 steps. Coexists with an LLM hot in memory — no eviction needed.
Image generation — quality FLUX.2 Klein 9B MLX-Q4 Jan 2026 115 s per image. Pre-quantized 4-bit MLX (8.9 GB, smaller than 4 B's FP16). Visibly sharper faces, fabric, text.
Music structure analysis SongFormer (ASLP-lab) Oct 2025 2.97 GB specialist transformer + MuQ SSL encoder. Beats Gemini 2.5 Pro on SongFormBench. Replaced our paid Gemini Flash pipeline.
Local audio LLM (general) — none — Gemma 4 e4b collapses every section to "chorus". Qwen2.5-Omni isn't in mlx-vlm yet. mlx-lm-omni has API rot.
The one-sentence headline

On a 32 GB Apple Silicon box, the winning recipe is 3–4 B-active MoE at NVFP4 quantization for text, distilled 4-step diffusion for images, and specialist transformers (not omni LLMs) for music — and the throughput ceiling is set by memory bandwidth, not parameter count.

01

The box and the rules of the game

Three physical constraints shape every model decision below. The Mac mini M4 is what it is; the trade-offs follow from the hardware.

  • Mac mini M4 — 10-core CPU, 10-core GPU
  • 32 GB unified memory — single pool, shared CPU/GPU
  • ~120 GB/s bandwidth — the binding constraint at decode time

Three constraints, in order of how often they decide things:

  1. Memory is the hard wall. Subtract ~6 GB for macOS, ~3–5 GB for the iPad sim and dev apps, and the largest LLM that comfortably co-resides is ~22 GB on disk (≈ 26 GB hot at 64 K context). Anything larger swaps to disk and throughput collapses.
  2. Decode is bandwidth-bound. Once weights are in memory, tokens-per-second is set by how fast active parameters stream through the GPU. MoEs win because only the active experts move per token.
  3. Prefill is compute-bound and quadratic in context. A 46 K-token document costs 9–11 minutes of prefill on every cold cache hit. Not tunable — that's physics on this rig.
02

General-purpose LLMs

4 axes

Tested across four task shapes that map onto how the box actually gets used: short-context reasoning, vision (screenshot Q&A), long-document summarization (≈ 46 K input tokens), and tool-call fidelity (the agentic loop).

Reasoning — 5-step word problem, ~180 input tokens

Decode throughput on a short-context reasoning prompt
tokens / second · M4 mini · NVFP4 or 4-bit MLX
Gemma 4 26B-A4B QAT 34.1
qwen-hermes (35B-A3B) 34.0
gemma-hermes (6.3B dense) 31.9
Qwen3-14B 4-bit 12.5
gemma4:12b-mlx 11.9
02041.5 (ceiling)tok/s
#ModelReleasedRuntime / quantDecode tok/sTTFTTotalQuality
01Gemma 4 26B-A4B QATJun 2026rapid-mlx · nvfp434.12.1 s31.1 s5/5/5
02qwen-hermes (35B-A3B)Apr 2026Ollama-MLX · nvfp434.011.9 s*70.7 s
03gemma-hermes (6.3 B dense)Mar 2025Ollama-MLX · /no_think31.925.4 s*64.4 s
04Qwen3-14B 4-bitMay 2025rapid-mlx · --no-thinking12.51.9 s98.3 s5/5/5
05gemma4:12b-mlxApr 2026Ollama-MLX · nvfp411.91.8 s88.9 s5/5/5

* cold-load TTFTs — model being swapped in. pure prefill on short prompts is 1–3 s.

Vision — LickBank app screenshot

Vision decode + image-token efficiency
Gemma 4's vision tokenizer is 4.3× more efficient than Qwen3-VL's
Gemma 4 26B-A4B QAT 34.0 t/s
Qwen3-VL-8B 4-bit 21.5 t/s
decode01734
Gemma 4 — image tokens 331
Qwen3-VL — image tokens 1,436
image tokens (less = better)01,500
#ModelReleasedDecode tok/sTTFTImage tokensQuality
01Gemma 4 26B-A4B QATJun 202634.03.0 s3315/5/5
02Qwen3-VL-8B 4-bitSep 202521.510.6 s1,4365/5/5
Gemma 4 12B-mlx (Ollama)Apr 2026Text-only — Ollama's MLX package ships without the vision projector.

Long-doc summarization — ~46 K-token Frankenstein excerpt

Long-context decode (46 K prompt tokens)
MoE prefill scales with active params, not total — the killer advantage
Gemma 4 26B-A4B QAT 22.2
gemma4:12b-mlx 9.7
Qwen3-14B 4-bit 4.8
decode tok/s01223

The MoE 26B-A4B at 46 K context decodes 2.3× faster than the dense 12 B and 4.6× faster than the dense 14 B. Its prefill (170 s) is also 3.2× faster than the 12 B's (546 s) — because Apple Silicon prefill scales with active params, not total.

Tool-call fidelity — 7-case suite (Hermes use case)

Tool-call decode rates — every MoE that fits clusters at the ceiling
7-case function-call suite · rapid-mlx · all hit 7/7 except Gemma-e4b
Nemotron-3-Nano 30B-A3B 41.8
Qwen3.6-35B-A3B (default) 41.5
GLM-4.7-Flash 35.2
Gemma 4 26B-A4B 33.0
Gemma 4 e4b · 6/7 31.7
Devstral-Small 24B dense · ✗ 6.3
tok/s02042
ModelReleasedArchitectureTool fidelityDecode tok/sCold prefill (~13 K)
Qwen3.6-35B-A3B 4-bitApr 2026MoE 3 B-active7/741.524.5 s
Nemotron-3-Nano 30B-A3BMar 2026MoE 3 B-active7/741.826.2 s
GLM-4.7-Flash 4-bitApr 2026MoE 3 B-active7/735.249.8 s
Gemma 4 26B-A4B 4-bitMay 2026MoE 3.8 B-active7/733.025.4 s
Gemma 4 e4b 4-bitMay 2026~4 B dense6/731.716.7 s
Qwen3-Coder-Next 4-bitFeb 2026MoE 80 BDisqualified — OOM at 42 GB > 32 GB unified
Devstral-Small-2505 4-bitMay 2025dense 24 BBroken — chat template ships without tool rendering; 6.3 tok/s anyway

Fidelity doesn't separate the fitting models. Every MoE that fits in 32 GB hits 7/7. The differentiator is decode speed, and decode speed is bandwidth-bound. Qwen3-Coder-Next (MoE 80 B, 42 GB at 4-bit) doesn't fit at all — Metal OOMs on first prefill.

Quick reference — our seven installed presets

~37 GB on disk total (three Qwen presets share NVFP4 blobs at 21 GB; three Gemma presets share theirs at 16 GB):

NameBaseBase releasedUse for
qwen-chatqwen3.6:35b-a3b-coding NVFP4Apr 2026Daily driver. 8 K ctx, think off. ~34 tok/s.
qwen-reasonqwen3.6:35b-a3b-coding NVFP4Apr 2026Multi-step reasoning. 64 K ctx, think on.
qwen-hermesqwen3.6:35b-a3b-coding NVFP4Apr 2026Hermes Agent default. 64 K ctx, tool-call sampling.
qwen-coding-fastqwen3.6:35b-a3b-coding NVFP4Apr 2026Long-context refactors. 32 K ctx, 16 K predict.
gemma-chatgemma4:26b-a4b NVFP4May 2026Prose, alternate voice. ~27 tok/s.
gemma-reasongemma4:26b-a4b NVFP4May 2026Tiebreaker vs qwen-reason.
gemma-agentgemma4:26b-a4b NVFP4May 2026Former hermes default. 256 K max ctx.
03

Image generation

6 tested · 2 kept

Stack: MFLUX (MLX-native FLUX/Qwen-Image/Z-Image port) at ~/.local/bin/mflux-*, weights cached under ~/.cache/huggingface/hub/, output to ~/imagen/. Step-distilled diffusion (4 steps, guidance 1.0, no negative prompt) dominates on Apple Silicon.

Seconds per 1024² image — lower is better
measured · q=4 · default model steps · log-scale visual emphasis
FLUX.2 Klein 4B · 4 steps 60 s
FLUX.2 Klein 9B · 4 steps 115 s
Z-Image-Turbo · 9 steps 218 s
Qwen-Image-2512 · 20 steps 1,300 s
deleted = washed022 min
ModelReleasedParamsDiskPeak RAMPer imageVerdict
FLUX.2 Klein 4BJan 20264 B22 GB6–8 GB60 sSkill default. Co-runs with LLM hot.
FLUX.2 Klein 9B MLX-Q4Jan 2026
pre-quant Apr 2026
9 B8.9 GB12–16 GB115 sQuality tier. Requires LLM eviction.
Z-Image-TurboNov 20256 B5.5 GB218 sDeleted. Beat on speed/quality curve by Klein 9B.
Qwen-Image-2512Dec 202520 B24 GBswap-bound1,300 sDeleted. Best text rendering — but 22 min/image.
FLUX.1-devAug 202412 BDownload stalled overnight repeatedly. Abandoned for Klein.
HiDream-I1May 202517 BToo tight on 32 GB; marginal gain over Klein 9B. Not pursued.

The key insight

Klein 9B's pre-quantized MLX-Q4 build is smaller on disk than Klein 4B's FP16 (8.9 GB vs 22 GB) but produces visibly sharper output. The size flip is because Klein 4B ships at full precision and quantizes at runtime via -q 4, while Klein 9B was pre-quantized by the community uploader. Do not pass -q 4 to Klein 9B — re-quantizing already-quantized weights degrades them measurably.

Memory math

Klein 4B (~6–8 GB peak) fits alongside gemma-hermes at 65 K context (~16–18 GB). Klein 9B (~12–16 GB peak) does not. The Hermes skill at ~/.hermes/skills/creative/mflux-local-images/SKILL.md issues ollama stop gemma-hermes before Klein 9B runs and lets the proxy's keep_alive ping reload the LLM on the next turn.

Prompting note

Klein is trained on natural-language descriptive prose, not tag soup. Push toward photo: camera + lens ("shot on Sony A7R V, 85mm f/1.4"), light ("golden hour", "overcast diffused"), texture ("skin pores visible", "fabric weave", "film grain"), genre ("candid photograph", "documentary"). Avoid CGI tells: "8k", "masterpiece", "highly detailed", "cinematic", "trending on artstation". FLUX.2 has no negative-prompt support — express exclusions in the positive prompt.

04

Music analysis

6 approaches

The use case: detect verse/chorus/bridge boundaries and label them for the Shreddy iPad practice app. A multi-model bake-off ended in an unexpected place — a 2.97 GB specialist transformer beat every general-purpose LLM and audio LLM we tried.

What we tested

ApproachReleasedVerdict
Gemini 2.5 Flash · audio-native, cloud Jun 2025 Strong labels but hit the free-tier 250 K-token/day cap at ~32 songs. Paid is ~$0.002/song — still cloud, still rate-limited, still external dependency.
Claude Vision · on spectrogram screenshots Worked but noticeably worse than audio-native LLMs. Spectrograms throw away too much signal.
Gemma 4 e4b · local audio LLM May 2026 Collapsed every section to "chorus". Unusable. Other small audio LLMs equally weak.
Qwen2.5-Omni · local audio LLM Mar 2025 Not loadable — mlx-vlm has no handler as of June 2026. mlx-lm-omni has API rot.
librosa HPSS + bar-snap · DSP only Surprisingly good boundary detection — 9 musically-sensible cuts on a Seether track. Fully local. But cannot label sections, just finds the cuts.
SongFormer · specialist transformer Oct 2025 Winner. 2.97 GB safetensors. MuQ SSL encoder + structure transformer. Beats Gemini 2.5 Pro on SongFormBench. 8-label vocab (intro, verse, chorus, bridge, inst, outro, silence, pre-chorus). Seconds per song on MPS.
Music Flamingo 8B · NVIDIA, GGUF Apr 2026 Kept on watchlist for narrative reasoning, but NVIDIA noncommercial license blocks commercial use.

The architecture lesson

This was the surprise of the whole project. We assumed an audio LLM would win — they're the new shiny — but a 2.97 GB specialist transformer trained on the actual task crushed both audio-native LLMs and image-vision LLMs operating on spectrograms. SongFormer's deterministic output (identical labels on CPU and MPS) is also operationally easier than any LLM call.

Production setup

Shreddy now ships with SongFormer in a Python 3.11 venv (the deps don't all support 3.14), patched model in the project tree at apps/data/models/songformer/, helper module at apps/scripts/songformer_inference.py. The librosa BPM/key/beats path runs unchanged in parallel. Anthropic and Google API key fields were deleted from the settings UI — they're no longer needed.

05

Models on the watchlist

8 candidates

Not installed today, but worth re-evaluating when their tooling or licensing changes. Each row names the specific gate that's blocking adoption.

ModelReleasedWhy we're watchingWhat needs to change
GLM-4.5-Air Jul 2025 Best published BFCL tool-call score (76.7%; Qwen is 75.7%) Needs a clean NVFP4 MLX build. Community GGUFs are ~25–30% slower on this rig.
Qwen3.6 with native MTP Apr 2026 1.4–1.6× decode on M4 Pro for dense 27 B per the spec mlx-lm PR #990 must merge into a release. No released runtime supports qwen3_5_moe native MTP today.
MTPLX MoE backend May 2026 1.71× verified on dense 27 B on this rig; awaiting MoE MTP-head support. youssofal/MTPLX#87 — needs a qwen3_next_moe_mtp backend for 46-tensor MoE heads.
Gemma 4 vision via rapid-mlx May 2026 26B-A4B QAT already wins vision; would want the 12 B's gemma4_unified path too. mlx-vlm 0.6.1 supports it but needs newer mlx-metal than brew supplies. Brew installs without a RECORD file, blocking pip upgrades.
Nemotron-3-Nano 30B-A3B Mar 2026 Ties Qwen3.6 on decode (41.8 vs 41.5 tok/s) and tool fidelity (7/7). Nothing — viable drop-in. Kept installed as backup; no reason to switch from a working setup.
Music Flamingo 8B Apr 2026 Could complement SongFormer with narrative/genre/mood description. NVIDIA noncommercial license — deal-breaker if Shreddy ever ships commercially.
Qwen2.5-Omni / mlx-lm-omni Mar 2025 The "general local audio LLM" slot is still vacant. mlx-vlm needs a Qwen2.5-Omni handler. mlx-lm-omni's API needs stabilizing.
Qwen3-Next 80B Feb 2026 Would lift the coding ceiling — if it fit. 42 GB at 4-bit exceeds 32 GB unified. Hard hardware ceiling — needs a 64 GB box.
06

The performance ceiling, explained

6 levers

The biggest realization from six months of benching: we are at the throughput ceiling for this hardware, and the levers people reach for don't move it. Here's the physics, summarized.

BANDWIDTH CEILING ≈ 41 tok/s 0 20 41 DECODE TOK/S 3B-active MoE 41 tok/s · AT CEILING dense 12B nvfp4 12 tok/s · more compute dense 27B + spec 10.6 tok/s · 1.71× over AR 3B MoE + spec 38 tok/s · 0.94× ❌ 3B MoE @ 3-bit 34.7 tok/s · slower than 4-bit

1 · Decode is bandwidth-bound, not compute-bound

On a 3 B-active MoE at 4-bit, generating one token requires reading roughly 1.5 GB of weights through memory. Apple's M4 mini has ~120 GB/s bandwidth. The arithmetic ceiling is 120 / 1.5 ≈ 80 tok/s; runtime overhead and KV-cache reads halve that to ~40 tok/s. Every 3 B-active MoE we tested clusters at 34–42 tok/s — Qwen, Nemotron, Gemma, GLM, all land there.

2 · MoE pays off twice — at decode and at long-context prefill

Decode reads only the active experts (~3–4 B params) regardless of total size. Prefill on Apple Silicon also scales with active-param count, not total. That's why the 26B-A4B QAT does long-doc summarization 3× faster than the dense 12 B Gemma — same prefill cost as a 4 B model, judged quality of a 26 B.

3 · Speculative decoding doesn't help compute-light models

Speculative decoding gives 1.4–1.7× on dense models — we measured 1.71× on Qwen3.6-27B-dense via MTPLX on this exact box. But its payoff scales with per-token compute, and a 3 B-active MoE is already compute-light. Grafted MTP on Qwen3.6-35B-A3B at depth 1 / 2 / 3 gave 0.94× / 0.73× / 0.58× — speculative makes the MoE slower. Confirmed by mlx-lm PR #990's author independently.

4 · Lower-bit quants are not free throughput

Tried Unsloth's UD-3-bit Qwen3.6-35B-A3B expecting a decode win. Got 34.7 tok/s vs uniform 4-bit's 41.5. The "3-bit" UD quant is mixed-precision: 270 layers at 8-bit, only 117 at 3-bit. In byte terms it's heavier than uniform 4-bit, and mixed-precision dequant kernels add overhead. MLX's 4-bit kernel is also far more optimized than its 3-bit path.

5 · The think toggle is the only 5× knob

Of all the sampling/runtime tweaks tested, only one moves wall-clock by a factor of five. Disabling thinking on Qwen3.6 cuts a 293 s response to 55 s with no decode-rate change — the model just writes 1,900 tokens instead of 9,500. Everything else (num_ctx, num_predict, temperature, sampling) is in the 1.0–1.5× range.

6 · The 32 GB wall

Even with NVFP4 quantization, the largest model that comfortably fits with a usable context window and KV cache is ~26 GB resident. Qwen3-Coder-Next at 42 GB is a hard out. Mixing two large models hot (e.g. gemma-hermes + Klein 9B image gen) requires explicit eviction. This isn't tunable — it's the hardware ceiling. A 64 GB box would unlock 80 B-class models; a 32 GB box won't.

Net

For text on this rig, Qwen3.6-35B-A3B-4bit at ~41 tok/s on rapid-mlx (or ~34 tok/s on Ollama-MLX) sits at the MLX throughput ceiling. There is no free throughput lever. Revisit only if (a) PR #990 ships and a higher-compute MoE appears, or (b) we upgrade the box.

07

Methodology and caveats

Caveats up front so the numbers above don't get used in ways they don't support.

  • Sample size: N=1 per axis on a single prompt for most benches. Decode tok/s numbers are physics — they're reliable. Quality ratings (5/5/5 etc.) are directional, scored by Claude as judge against a rubric.
  • TTFT contamination: "Cold prefill" numbers when the model is being swapped in include load time, not just prefill. Pure prefill cost on short prompts is 1–3 s for any of these models.
  • Quant comparison isn't apples-to-apples: Gemma's NVFP4 vs Qwen's uniform 4-bit are different formats. Some Gemma 4 wins come from NVFP4 being newer and better-tuned for Apple Silicon than the older 4-bit MLX kernel.
  • Same-family judge bias: Claude judging models has a possible bias toward Claude-flavored output. The reasoning axis has a ground-truth answer (10:22 AM, 137 mi on the train problem) so quality scores there are grounded; doc summary and vision scores rely on Claude's read.
  • Tooling churn: rapid-mlx, mlx-vlm, mlx-metal, and Ollama-MLX all moved meaningfully between Apr and Jun 2026. Numbers tagged to specific versions. Re-run before relying on a six-month-old measurement.

Bench scripts (for reproducibility)

Every script that produced a number on this page is checked into ./bench/. Walk-through and run instructions in the bench/README.

ScriptWhat it measures
bench_3axis.pyReasoning + vision + long-doc decode/TTFT against rapid-mlx /v1. Streams SSE, captures content + reasoning.
judge_3axis.pyLLM-as-judge wrapper around the local claude -p CLI. Scores 1–5 against a rubric.
bench_toolcall.py7-case single-turn function-calling suite + cold prefill on a Hermes-sized prompt.
bench_server_ttft.pyCold-prefill TTFT on a ~13 K-token system prefix.
bench_gemma_swap.pyGGUF q8_0 vs NVFP4 MLX delta on a single coding prompt against Ollama.
bench_qwen_hermes.pyTool-call sampling sweep validating the Qwen team's published recipe.
local_bench_mlx.py5-prompt coding bench against Ollama's MLX backend.
gemma4_mlx_bench.pyOriginal Gemma 4 12 B MLX harness (pre-26B-QAT generation).

Raw data + reports

Raw run JSON and the markdown reports the post draws from are in bench/results/:

Each *.json in the same directory has prompt, comp_tok, ttft_s, total_s, decode_tps, and the full streamed content — so any score can be re-judged with a different rubric or model.