Inference Engines & Runtimes
llama.cpp, transformers.js with WebGPU, mobile-native runtimes, and vLLM/TGI — matching the runtime to where the model actually has to run
Inference Engines & Runtimes
TL;DR
A quantized model file is inert until something loads and runs it, and the right runtime depends entirely on the target: llama.cpp for CPU/local, transformers.js + WebGPU for the browser, mobile-native runtimes for phones, and vLLM/TGI when a small model is still served at scale behind an API. Picking a runtime is not a preference — it's dictated by where the model has to live.
| Property | Value |
|---|---|
| Level | Intermediate |
| Reading time | ~18 minutes |
| Prerequisites | Quantization for Edge |
| You will understand | Which runtime fits which deployment target, and the real trade-offs between them |
The Decision, Up Front
Runtime by target — start here
llama.cpp (GGUF)
RecommendedCPU, laptops, self-hosted servers, "just run it locally" cases. No Python or CUDA required, OpenAI-compatible API server included.
transformers.js + WebGPU
Runs entirely inside a browser tab. Zero server, zero install. The newest fully-viable option for this deployment shape.
MLC-LLM / Core ML / NNAPI
Mobile apps (iOS/Android) needing the best on-device performance the platform allows, at the cost of a real per-platform build step.
vLLM / TGI
GPU server-side, high concurrent throughput — the small model is still deployed behind an API, not on the end user's device.
The rest of this page goes through each in enough depth to actually choose.
llama.cpp: The CPU/Local Default
llama.cpp is a C/C++ inference engine built specifically for running GGUF models with no heavyweight dependencies — no Python interpreter, no CUDA toolkit, no framework installation. That makes it the natural choice whenever "local" means "someone's laptop" or "a lightweight self-hosted box," where you can't assume a GPU or a Python environment is available.
# Point it at a Hugging Face repo path and filename directly
llama-cli -hf TheBloke/some-model-GGUF:Q4_K_M -p "Explain memory mapping in one sentence."It runs well on CPU (with AVX/AVX2/AVX512 vector instructions where the hardware supports them) and can also use GPU acceleration (CUDA and others) when one is present — the same binary scales from a phone-class ARM chip to a desktop with a discrete GPU. It also exposes an OpenAI-compatible API server (llama-server), so existing client code written against the OpenAI chat completions format can point at a local llama.cpp server with essentially no changes.
Hugging Face Inference Endpoints can deploy a llama.cpp container directly from a GGUF model repo, which is a useful middle ground: the same artifact and runtime you'd use fully on-device, but hosted, if you want a small model served cheaply without committing to vLLM/TGI's heavier GPU-serving stack.
transformers.js + WebGPU: The Browser
This is the newest deployment shape to become genuinely practical: a model running inside a browser tab, with no server involved at any point after the page and model files are fetched.
import { pipeline } from "@huggingface/transformers";
const extractor = await pipeline(
"feature-extraction",
"mixedbread-ai/mxbai-embed-xsmall-v1",
{ device: "webgpu" },
);
const embeddings = await extractor(["Hello world!"], { pooling: "mean", normalize: true });The device: "webgpu" option is the entire change from a normal transformers.js pipeline call — it's built on a collaboration with ONNX Runtime Web, which does the actual GPU-accelerated execution inside the browser sandbox. This works across modalities, not just text: text embedding (as above), automatic speech recognition (e.g. Whisper), and small vision-language models such as HuggingFaceTB/SmolVLM-256M-Instruct all run this way.
WebGPU support is not universal yet. Chrome and Edge (113+) have solid support; Firefox and Safari have historically needed feature flags enabled in some versions. A production browser deployment needs feature detection and a graceful fallback (a smaller CPU-backed WASM path, or a message asking the user to switch browsers) — don't assume WebGPU is simply available.
This is the runtime behind the fully local, in-browser assistant idea in Project Ideas — no backend to operate, no per-request cost, and the data never leaves the tab.
Mobile-Native Runtimes
Phones reward a real per-platform build step in exchange for the best performance the hardware allows:
| Runtime | Platform | Approach |
|---|---|---|
| MLC-LLM | Cross-platform (including mobile) | Compiles the model ahead-of-time for the target; ships pre-quantized variants (e.g. 4-bit/8-bit) for common models |
| Core ML | iOS/macOS | Apple's native runtime; uses palettization (k-means weight clustering) as its own quantization approach, distinct from GGUF's block quantization |
| NNAPI | Android | The platform's native neural network acceleration API |
Unlike llama.cpp or transformers.js, these generally aren't "point the runtime at a file and go" — they involve a real export/compile step tied to the specific target, and the resulting artifact is usually platform-specific rather than a single file that works everywhere.
A model quantized with Core ML's palettization is not interchangeable with a GGUF Q4_K_M file — they're different quantization schemes for different runtimes. Decide your runtime first, then get the model into that runtime's expected format, not the other way around.
vLLM / TGI: When "Small" Still Means "Served at Scale"
Not every small-model deployment is on-device. A 1-4B model served behind an API to thousands of concurrent users is still very much a small-model deployment — it just uses the GPU-serving stack instead of an edge runtime.
vLLM and Hugging Face's own TGI (Text Generation Inference) are built for throughput: batching many requests together, using AWQ/GPTQ/fp8-quantized weights (see Quantization for Edge), and optimizing for tokens-per-second across the whole fleet rather than latency on a single request. This is the right choice when the constraint driving "small" is cost per request at scale, rather than "must run on this specific device."
Matching Runtime to Constraint, Not to Familiarity
The actual decision process
The most common mistake here is picking the runtime last. Teams quantize a model, then discover the format they chose doesn't run well on the actual target device or browser. Decide the deployment target and its runtime before choosing a quantization format — the runtime should drive the quantization choice, not the other way around.
Concept Checks
Check yourself
You're building a tool that must work on a user's laptop with no internet connection and no assumption of a GPU. Which runtime fits, and why?
llama.cpp — it needs no Python, no CUDA, and no framework installation, runs well on CPU alone, and its GGUF format is designed for exactly this local, dependency-light case. transformers.js+WebGPU would also technically run locally, but only inside a browser context, which may not fit a standalone desktop tool.
Why can't you assume WebGPU is simply available in a production browser deployment?
Browser support isn't universal — Chrome/Edge have solid support from version 113+, but Firefox and Safari have historically required feature flags in some versions. A production deployment needs feature detection and a fallback path (a WASM-backed CPU path, or guidance to switch browsers), not an assumption that every visitor's browser supports it.
A model quantized with Apple's Core ML palettization — can you load that same file directly in llama.cpp?
No. Core ML's palettization (k-means weight clustering) and GGUF's block-based K-quants are different quantization schemes tied to different runtimes. The model has to be exported/quantized specifically for the runtime you intend to use — the artifacts aren't interchangeable across runtimes.
Your small model is going to serve thousands of concurrent API users, not run on individual devices. Does the on-device runtime advice in this page still apply?
Not directly — that's a GPU-serving scenario, where vLLM or TGI (optimized for batched throughput across many requests, using AWQ/GPTQ/fp8) is the right tool, not llama.cpp or a mobile-native runtime. "Small" here is about cost-per-request at scale, not fitting on one constrained device.
Key Concepts Recap
| Concept | One-line summary |
|---|---|
| llama.cpp | C/C++ GGUF runtime, no Python/CUDA needed, CPU+GPU, OpenAI-compatible server |
| transformers.js + WebGPU | In-browser inference, device: "webgpu", no server involved |
| ONNX Runtime Web | The engine transformers.js's WebGPU support is built on |
| MLC-LLM | Cross-platform compiled on-device runtime, pre-quantized model variants |
| Core ML palettization | Apple's k-means-based quantization, distinct from GGUF |
| NNAPI | Android's native on-device acceleration API |
| vLLM / TGI | GPU-serving engines for high-throughput API deployment, not on-device |
| Runtime-first decision order | Pick the deployment target and runtime before the quantization format |
Next
Runtime chosen — now the full on-device deployment story, from quantized file to shipped app: On-Device & Edge Deployment.
Quantization for Edge
GGUF quant types, AWQ/GPTQ, bitsandbytes, and BitNet — how to shrink a small model further, and which format fits which device
On-Device & Edge Deployment
The full stack from quantized model file to a shipped offline app, and the constraints — RAM, battery, download size, versioning — unique to running on someone else's device