Free MIT · 100% Swift · zero dependencies

LLMs at the speed
of unified memory.

MLX Runner is a native macOS app to discover, download and chat with MLX models from Hugging Face. Its inference engine is written from scratch in Swift and Metal. No Python, no packages, no copy of your weights.

Apple silicon · macOS 15 or newer

Qwen2.5 0.5B Instruct · 4-bit

Why is generating tokens memory-bound?

Generating a token means reading every weight once. A 4-bit 7B model is about 4 GB, so a 197 GB/s memory bus caps decode near 48 tokens per second. MLX Runner gets close to that cap: one fused Metal kernel dequantizes 4-bit weights and multiplies in a single pass. q, k and v are one matvec and gate and up another. RMSNorm, RoPE and the KV-cache append run as one kernel. Attention uses online softmax across simdgroups. The weights are memory-mapped and wrapped in Metal buffers without a copy, so nothing sits between unified memory and the GPU. Small models feel instant. Big ones run close to what the hardware allows.

decode
285 tok/s
tokens
0
elapsed
0.00 s
Replayed in your browser at the decode speed mlxr bench measured for each model on an M2 Pro (16 GB). The text is ours; the pace is the model's.

Same prompt. Five models. Real speeds.

A 512-token prompt and a 256-token answer, paced by the prefill and decode rates mlxr bench measured on an M2 Pro (16 GB). Cyan is prefill, the gradient is decode. Watch where the time goes as the weights grow.

  1. Qwen2.5 0.5B 278 MB1.07 s
  2. Qwen3 0.6B 336 MB1.29 s
  3. Llama 3.2 1B 695 MB1.84 s
  4. Phi-3.5 mini 2.15 GB5.54 s
  5. Mistral 7B v0.3 4.08 GB9.72 s
Measured with mlxr bench on an M2 Pro (16 GB). All models 4-bit; decode with greedy sampling.
ModelWeightsPrefill (512 tok)Decode @16Decode @1K ctx
Qwen2.5 0.5B Instruct278 MB2,970 tok/s285 tok/s258 tok/s
Qwen3 0.6B336 MB2,030 tok/s247 tok/s204 tok/s
Llama 3.2 1B Instruct695 MB1,170 tok/s183 tok/s165 tok/s
Phi-3.5 mini2.15 GB320 tok/s65 tok/s57 tok/s
Mistral 7B v0.34.08 GB166 tok/s38.6 tok/s36.6 tok/s

The 7B ceiling

Every token reads every weight. Once.

Decoding one token streams the whole model through the GPU, so speed is capped by memory bandwidth, not by compute. This M2 Pro measures 197 GB/s with a plain read kernel, which caps a 4.08 GB model at about 48 tok/s.

MLX Runner decodes Mistral 7B at 38.6 tok/s: about 82% of that cap. Going faster takes more bandwidth (M-series Max chips have about 400 GB/s), fewer bits per weight, or speculative decoding, which is implemented and opt-in (mlxr run --spec).

Mistral 7B decode uses about 82% of the measured 197 GB/s memory bandwidth. ~82% of measured bandwidth
Read bandwidth
197 GB/s
Ceiling, 4.08 GB
≈ 48 tok/s
MLX Runner
38.6 tok/s

An engine written from scratch, for Metal.

Apple's mlx-swift is a third-party package, so MLX Runner doesn't use it. It runs MLX models on its own kernels instead, from the same files mlx-lm reads. Any compatible mlx-community repo works without conversion.

One decode step, one command buffer
  1. Unified memorysafetensors, mmaped, wrapped in Metal buffers with no copy
  2. q · k · vone fused 4-bit matvec over three matrices, bias folded in
  3. Norm · RoPE · KVper-head RMSNorm, RoPE and KV-cache append as one kernel
  4. Attentiononline softmax across 16 to 32 simdgroups
  5. gate · up · downfused matvecs; the residual add is fused with the next RMSNorm
  6. Next tokensampled, streamed, appended to the KV cache

Runs today

  • qwen3thinking toggle
  • qwen2q/k/v biases
  • llamaLlama 3 RoPE scaling
  • mistralSentencePiece BPE
  • phi3LongRoPE

Each verified end to end, with prefill and token-by-token decode checked for the same next-token distribution. Not yet: Gemma, mixture-of-experts and vision models; Discover marks them as not runnable.

A Mac app, not a wrapper.

SwiftUI with Liquid Glass on macOS 26. Search the Hub, download with verified checksums, chat with Markdown and code, and serve the loaded model to your tools.

MLX Runner chat: a streamed reply with Markdown and code, its tok/s, token count and time to first token, next to the sampling inspector.

Chat

  • Streaming Markdown: tables, quotes, highlighted code with copy.
  • Live tok/s, token count and time to first token on every reply.
  • Qwen3 thinking in a collapsible block, with a toggle to turn it off.
  • Edit and resend, regenerate, per-chat system prompt, searchable history.
  • Inspector for temperature, top-p, top-k, min-p, repeat penalty and context length.

Command line

mlxr, the same engine in your terminal.

Search, download, run, serve and benchmark from a shell. It shares the app's model library, and every number on this page came from mlxr bench.

  • mlxr search qwen3
  • mlxr download mlx-community/Qwen3-4B-4bit
  • mlxr run <model> -p "Explain RoPE" --no-think
  • mlxr serve <model> --port 1234
  • mlxr bench <model>
zsh · M2 Pro
$ mlxr run mlx-community/Qwen2.5-0.5B-Instruct-4bit -p "Write a haiku about Metal shaders." --temp 0
Loaded Qwen2.5-0.5B-Instruct-4bit [qwen2, q4g64, 24 layers, chatML] in 0.55s
[prompt: 37 tokens, 953 tok/s, ttft 0.039s]
Metal shaders: a new art,
Transforming code into magic,
A new form of art.
[generated 19 tokens at 276.3 tok/s, stop: stop, drafts accepted 0/0]

$ mlxr bench mlx-community/Qwen2.5-0.5B-Instruct-4bit
# … kernel micro-benchmarks …
prefill   128 tokens:  2830.5 tok/s
prefill   512 tokens:  2968.7 tok/s
prefill  2048 tokens:  2645.0 tok/s
decode @  16:  283.8 tok/s  (wall 3.52 ms, gpu 3.02 ms per token)
decode @1024:  252.4 tok/s  (wall 3.96 ms, gpu 3.36 ms per token)
Real output, captured on an M2 Pro (16 GB) and replayed.

Your tools already speak its API.

The local server is OpenAI-compatible, including the Responses API with typed streaming events and the current Chat Completions spec. Point any OpenAI SDK at it.

# Responses API, streamed
curl http://localhost:1234/v1/responses \
  -H "Content-Type: application/json" \
  -d '{"model": "Qwen3-0.6B-4bit", "stream": true,
       "input": "Explain RoPE in one paragraph."}'

# Or any OpenAI SDK
client = OpenAI(base_url="http://localhost:1234/v1",
                api_key="not-needed-on-localhost")
  • Safe by default. Binds to localhost and rejects other Host headers, so DNS rebinding can't reach it.
  • No drive-by browsers. Unknown browser Origins are refused; JSON bodies only.
  • Key on the LAN. An API key is optional on localhost and required in LAN mode.
  • Bounded. Timeouts, connection, queue and size limits.
  • Honest errors. Tools, JSON mode, images or logprobs get an OpenAI-style 400 instead of being silently ignored.
  • Reasoning included. Qwen3 thinking comes back as reasoning_content or a reasoning item.

Model files are untrusted input.

MLX Runner runs files from the internet on your GPU and can open a port. It is built like it knows that.

Get MLX Runner

For Apple silicon Macs running macOS 15 or newer. Free and open source, MIT licensed.

Release details load from GitHub. Without them, every link opens the latest release on GitHub.

First launch

Open the DMG and drag MLX Runner to Applications. The app is ad-hoc signed, not notarized by Apple, so macOS blocks the first launch. Right-click the app and choose Open, or run:

xattr -dr com.apple.quarantine "/Applications/MLX Runner.app"

After that, new versions arrive through MLX Runner → Check for Updates…, which verifies each one before installing it. Prefer to check it yourself first? That's next.

Verify before you run it

Every release is built by GitHub Actions from a tagged commit on a clean runner. Three independent checks prove the file you have is that build: its checksum, the release signature, and where it was built.

  1. Download the app, the checksum list and its signature

    curl -LO https://github.com/BackendStack21/mlx-runner/releases/download/<tag>/MLX-Runner-<version>-macos-arm64.dmg
    curl -LO https://github.com/BackendStack21/mlx-runner/releases/latest/download/SHA256SUMS
    curl -LO https://github.com/BackendStack21/mlx-runner/releases/latest/download/SHA256SUMS.sig

    For the CLI, download mlxr-<version>-macos-arm64.tar.gz instead; every step below works the same.

  2. Check the SHA-256

    shasum -a 256 -c SHA256SUMS --ignore-missing

    Expect MLX-Runner-<version>-macos-arm64.dmg: OK. It must also match the hash in the table above.

  3. Check the release signature

    curl -fsSLo mlx-runner.pub https://raw.githubusercontent.com/BackendStack21/mlx-runner/main/packaging/update-key.pub
    openssl pkeyutl -verify -pubin -inkey mlx-runner.pub -rawin -in SHA256SUMS -sigfile SHA256SUMS.sig

    Expect Signature Verified Successfully. Needs OpenSSL 3 or newer (brew install openssl; the built-in one is LibreSSL). This is the same Ed25519 signature the in-app updater checks before installing anything.

  4. Check where it was built

    # GitHub CLI: https://cli.github.com
    gh attestation verify MLX-Runner-<version>-macos-arm64.dmg --repo BackendStack21/mlx-runner

    Checks a Sigstore-signed SLSA provenance statement in a public transparency log. It proves the file came out of this repository's release workflow at a specific commit, and it doesn't depend on this website.

  5. Install the CLI (optional)

    curl -fLO https://github.com/BackendStack21/mlx-runner/releases/download/<tag>/mlxr-<version>-macos-arm64.tar.gz
    curl -fLO https://github.com/BackendStack21/mlx-runner/releases/latest/download/SHA256SUMS
    shasum -a 256 -c SHA256SUMS --ignore-missing
    tar -xzf mlxr-<version>-macos-arm64.tar.gz
    sudo mkdir -p /usr/local/bin
    sudo install -m 755 mlxr-<version>-macos-arm64/mlxr /usr/local/bin/mlxr
    mlxr list

    Run it in an empty folder; it works on its own, without steps 1–4. /usr/local/bin doesn't exist on a new Mac, hence the mkdir. Any directory on your PATH works. Downloaded with a browser instead? Safari may unpack the archive for you, so skip the tar line, and clear the quarantine flag afterwards: xattr -d com.apple.quarantine /usr/local/bin/mlxr.

Or build it yourself

You need an Apple silicon Mac with macOS 15 or newer and Xcode or its command-line tools (Swift 6). There are no dependencies to fetch.

git clone https://github.com/BackendStack21/mlx-runner.git
cd mlx-runner && git checkout <tag>
make test
make app    # build/MLX Runner.app
make cli    # the mlxr tool