# Mac mini follow-up to the Hugging Face guides

Run date: 2026-10-31. This directory supplements the [Linux evidence](/blog/best-hugging-face-tts-models-2026); it does not replace it. These are small functional observations on a shared desktop, not representative quality or production-capacity benchmarks. Robotics remains source-based: no policy loading, simulated rollout or physical robot trial was performed.

## Completed observations

| Task | Actual execution | Outcome |
| --- | --- | --- |
| Kokoro ONNX | Mac CPU, four threads, two stock voices, two scripts, three timed repetitions each | Median synthesis 2.133–2.675 s; 12 WAVs, model/voice binaries match Linux |
| Qwen3-TTS 0.6B CustomVoice | Community bfloat16 conversion on MLX Metal; stock Ryan; two scripts, three repetitions each | Narration median 5.765 s for 12.96 s audio; stress median 6.241 s for 14.16 s audio |
| Whisper tiny.en | CTranslate2 CPU INT8, beam five; original three Linux WAVs, three repetitions each | Median 0.283–0.374 s; 0/32 word errors in every run on this easy intended synthetic reference |
| Whisper large-v3-turbo | Existing community MLX FP16 conversion, Metal, greedy; same three original WAVs, three repetitions each | Median 0.421–0.438 s; 0/32 errors in every run; changes model/runtime/decoder/accelerator |
| Granite Docling | PyTorch MPS float32; original invoice; warm-up plus three runs | Median 30.429 s, 317 tokens; all three raw outputs byte-identical to Linux; no cap hit |
| LightOnOCR-2-1B | Documented explicit LightOn model/processor classes, PyTorch MPS float32; same invoice | Median 6.019 s, 185 tokens; correct labeled values and table rows on this page; no cap hit |
| Wan2.2-TI2V-5B conversion | Retained community mixed Q8/bfloat16 checkpoint, MLX-Gen 0.38.0 / MLX Metal | One 512×320, 49-frame, 24 fps silent clip; full elapsed 353.732 s; functional preview with cropped mug and slight highlight wobble |

The corresponding JSON retains individual times rather than just these rounded medians. All successful timed observations and outputs are kept. ASR observations reuse one underlying script; they are not independent speech examples. OCR results reuse one page; they are not an extraction-accuracy benchmark. The video has no repeated timing or separate warm-up.

## Machine, isolation and resource measurement

`hardware.json` records the actual Apple M6 Mac mini, 32 GB unified memory, macOS 27.0.1 (26A434). Hardware serial numbers and unique identifiers were removed. Python environments and all model weights are outside the repository. The new environment reuses the existing video-lab Python 3.12 site-packages through a `.pth` file and adds CPU inference/vision packages in its own site-packages; the existing environment was not changed. Per-task JSON records Python and package versions, including the setup's evolution when Pillow and Torchvision were added. The new environment is `/Users/dodo/code/hf-mac-evaluation-2026-10-31/.venv`; the reused environment is `/Users/dodo/code/video-lab/.venv`. No model server or dev server was started.

Tasks run serially under the local media lab's existing shared lock and resource guard. Existing model downloads and other queued work are allowed to finish. The guard retains its 40 GiB disk reserve, approximately 27 GiB process footprint and 5 GiB additional system swap limits. No other worker was stopped. Resource JSON records process-tree RSS and physical footprint sampled through macOS `proc_pid_rusage`, plus system swap increase. The guard's outer elapsed time includes process startup, imports, any required download, loading, warm-up and repetitions; it is **not** the inference timing used in article tables. Waiting for the shared lock is excluded.

Each task also records macOS `getrusage` peak process RSS. MLX records its allocator peak; PyTorch MPS records driver/current allocations at the end of the task (these are not peaks). These metrics describe different memory accounting boundaries. None establishes a minimum memory requirement or equals the machine's entire unified-memory consumption.

## Reproduction

Read the machine's `video-lab/AGENTS.md` and media-lab instructions before running. Reuse the installed runtimes and caches, wait for an idle machine, and run one task at a time. The checked-in runner uses the exact committed Linux audio and invoice image, never regenerating the invoice through a platform-specific font.

```sh
# From the repository root, using an isolated external environment:
python scripts/model-guides/sample-mac.py tts --output docs/marketing/data/hugging-face-mac-2026-10-31
python scripts/model-guides/sample-mac.py asr-cpu --output docs/marketing/data/hugging-face-mac-2026-10-31
python scripts/model-guides/sample-mac.py asr-mlx --output docs/marketing/data/hugging-face-mac-2026-10-31
python scripts/model-guides/sample-mac.py granite --output docs/marketing/data/hugging-face-mac-2026-10-31
python scripts/model-guides/sample-mac.py lighton --output docs/marketing/data/hugging-face-mac-2026-10-31
python scripts/model-guides/sample-mac.py qwen --output docs/marketing/data/hugging-face-mac-2026-10-31
```

The output directory must contain the supplied `sources.json` to resolve the pinned community conversions and LightOn revision. The runner defaults to three timed repetitions after a separate warm-up. A fresh process is used per task. Model downloads, manifests and loading occur before inference timers. `*-files.json` records SHA-256 for every file in a downloaded snapshot, including tokenizer and configuration assets. Kokoro records its two selected binary hashes directly. Preserve every repeated output rather than selecting a best take.

For a standalone downloaded runner, use `--audio-input /path/to/original-wavs` and `--image-input /path/to/sample-invoice.png` instead of relying on checkout-relative defaults. Download `sources.json` into the selected output directory first. The fixed input paths are intentionally separate from generated Mac outputs: new TTS samples must never silently become the ASR inputs. WAVs are moved into the distinct public Mac audio directory after generation; the result JSON retains their filenames and hashes.

## Interpretation by task

**Kokoro:** identical ONNX export, voices (`af_heart`, `am_michael`), literal scripts, speed 1.0 and four CPU threads as Linux. This is ONNX CPU execution on Apple Silicon, not GPU inference. Timings include phonemization and synthesis but exclude WAV saving. PCM-16 output remains 24 kHz. No reference voice is cloned.

**Qwen3-TTS:** the 0.6B **CustomVoice** checkpoint, through the explicitly named community bfloat16 MLX conversion, uses stock speaker Ryan and English. It is a different size from the 1.7B checkpoints already discussed in the article. It is not a Base cloning or VoiceDesign test. Seed 42 is reset for each repetition; sampling and token settings are retained. Timers fully consume generated chunks, materialize waveforms and synchronize Metal before stopping. Saving is excluded. Comparison with Kokoro changes model, voice, runtime, precision and accelerator; it cannot isolate the effect of hardware or establish listener preference.

**ASR:** the three original Linux narration WAVs, including the seeded 15 dB noisy derivative, are read directly from the existing public paths. The intended narration has 32 normalized words. Each result retains the complete transcript, reference, input SHA-256, numerator and denominator. Exact normalization is `re.findall(r"[a-z0-9]+(?:'[a-z0-9]+)?", text.lower())`, followed by word-level Levenshtein distance. The intended synthetic script is not an independently human-audited transcript. These correlated inputs establish neither population WER nor meeting accuracy. CPU uses the same pinned tiny.en CTranslate2 INT8 conversion as Linux, four threads and beam five. Turbo uses the pre-existing MLX float16 conversion of OpenAI large-v3-turbo on Metal, English, temperature zero and greedy decoding. Preflight inspection found that mlx-audio 0.6.1 explicitly rejects beam search, so beam five is not claimed for that run. Differences between those rows change model, runtime, precision, decoder settings and accelerator. Timings include input decoding, float32 polyphase resampling from 24 to 16 kHz, checksum reading and complete transcription. Loading/downloads are excluded. Each runtime warms up on a complete clean-heart transcription.

**OCR:** both models receive the same committed 1000×720 invoice. Granite uses the Linux checkpoint revision and Docling prompt, float32, greedy decoding, SDPA and a 1,024-token cap, now on PyTorch MPS with the PIL image-processing backend. LightOnOCR-2-1B uses its official documented MPS float32 path, explicit `LightOnOcrProcessor` and `LightOnOcrForConditionalGeneration` classes, Pixtral image processing with Torchvision, and an image-only transcription prompt. It is the smaller **previous generation**, not LightOnOCR-3. One complete inference is warm-up, then three timed runs synchronize MPS. Timings exclude preprocessing, output decoding/saving, loading and downloads. Raw special-token output is preserved. Exact value presence is reported separately from editorial inspection of field associations in `ocr-field-review.json`; overlapping strings (`49.00` inside `249.00`) alone cannot prove two correct line items. Inspect truncation, end markers, labels and complete rows before assessing correctness.

**Video:** `wan-resource.json` retains the exact helper command and full process elapsed time; `wan-metadata.json` includes the runtime's negative prompt, UniPC solver, 20 steps, seed 42, guidance 5, flow shift 5, 1 GiB cache limit and streamed VAE decode. `wan.json` records prompt/output SHA-256 and full-frame decoding validation. The all-49-frame contact sheet is `wan-contact.png`. The clip crops the mug's rim and part of its body, with subtle framing drift and highlight wobble, so it is not an approved product shot. No audio stream exists. The runtime's documentation treats canvases below 832×480 as functional checks rather than visual-quality settings. The full guard elapsed includes loading and saving; no separate loading timer exists. Download time and queue wait are excluded. Sampled peak physical footprint was 14.418 GiB, sampled RSS 9.648 GiB, MLX allocator peak 12.976 GiB and added system swap 0 GiB; these are distinct accounting boundaries, not minimum memory requirements.

**Audio inspection:** `audio-validation.json` records full PCM decode, nonempty/finite samples, rates, durations and verified output hashes for every timed WAV. `tts-content-inspection.json` records an additional independent tiny.en transcription of each first TTS take. It transcribes “publish” for the scripted “published” in Qwen's narration and varies numeral punctuation/Teamday spelling in stress scripts. These are review cues, not established synthesis failures or a listener score. The exact scripts and unedited first takes are published beside playback controls; all repetitions remain downloadable.

## Failed setup attempts and preflight decisions

- Granite's first attempt stopped at `ModuleNotFoundError: No module named 'PIL'`, before model loading/inference. Pillow 13.0.0 was added only to the isolated environment. `granite-setup-failure.txt` and its resource record preserve the failure; `granite-resource.json` describes the successful retry.
- LightOn's first attempt downloaded its pinned checkpoint but stopped before inference because the generic Pixtral processor required missing Torchvision. Torchvision 0.29.2 (declares Torch >=2.14 compatibility; existing Torch is 2.14.1) was installed without replacing Torch. The retry follows the card's explicit LightOn classes; the checkpoint's older metadata selects generic Pixtral/Mistral3 auto classes otherwise. `lighton-setup-failure.txt` and its resource record preserve this setup failure. `lighton-resource.json` describes the successful retry.
- Installed mlx-audio 0.6.1 explicitly raises `NotImplementedError` for Whisper beam search. This was caught in preflight before the measured run; greedy decoding is recorded for Turbo. There is no claimed beam-five MLX timing.
- OmniVoice was excluded in preflight for the upstream/conversion license conflict below. It was not measured or downloaded for this task.

Neither setup failure establishes that its model cannot run on Macs; both retries completed. No failed or abandoned generation is hidden behind a selected best take.

## Source and license decisions

`sources.json` records date, revisions, source-response hashes and saved primary model cards. Current source checks apply only to the models examined in this follow-up; the earlier shortlist snapshots retain their original scope.

- OmniVoice was considered because a working local conversion was cached, but **was not run** for this comparison. The conversion's Apache metadata conflicts with the original `k2-fsa/OmniVoice` model card, which states CC-BY-NC for weights. It was excluded from the permissively licensed second-model test. No rights are inferred from the conversion metadata.
- Qwen's 0.6B CustomVoice source and the selected community conversion declare Apache 2.0. The stock speaker interface avoids reference-audio cloning.
- The conversion names `Wan-AI/Wan2.2-TI2V-5B-Diffusers` as its exact upstream source, within the Wan2.2-TI2V-5B release family. Both upstream checkpoints and the retained community mixed Q8/bfloat16 conversion declare Apache 2.0. The checkpoint card links a missing Hub `LICENSE.txt`; `Wan2.2-LICENSE.txt` preserves the full Apache text from the pinned official Wan2.2 code repository, with its URL and checksum in `sources.json`.
- HunyuanVideo and H3 were not run for this article: the earlier checked community licenses exclude the EU. No gated model conditions were accepted or bypassed. The existing LTX tools were inspected but are not needed for the bounded Apache-licensed Wan test.

Playable samples use distinct Mac paths, controls and no autoplay. The original Linux public files and JSON remain intact. These external checkpoint tests do not establish Teamday integration.

## Public artifact paths

- Audio, including all three repetitions: `/audio/hugging-face-models-mac-2026-10-31/`.
- Original MP4: `/videos/hugging-face-models-mac-2026-10-31/wan-mug.mp4`.
- Settings, transcripts, raw OCR, review notes, resource records and runner: `/samples/hugging-face-models-mac-2026-10-31/`.

`SHA256SUMS` in the public sample directory inventories the published evidence and Mac media, excluding itself. The repository's Linux artifacts were checked against their existing hashes and were not replaced.
