macOS

Render Essence 2 and Expression 2 on a Mac with Apple silicon, in a Mac app or from a terminal, with the same package as iPhone and iPad.

Renders on the device Apple silicon API secret Swift package 2.19.0

The same Swift package that runs on iPhone and iPad renders the avatar on a Mac with Apple silicon: in a Mac app, or in a command-line tool with swift run. No device, provisioning profile or entitlement is needed to try it from a terminal.

Captured on an Apple M4 iMac (macOS) · Swift package 2.14.2 · wise-pup (Expression 2) · 2026-09-23.

Before you start

You needExpression 2Essence 2
A MacApple silicon, macOS 13 or newerApple silicon M3 or newer, macOS 26 or newer
ToolchainXcode 26 or newer (to build)the same
Credentialan API secret (Creator plan or higher)the same
Diskabout 800 MB for the example: downloads plus the folder the engine unpacks them intoabout 250 MB of downloads

Install

In Xcode choose File → Add Package Dependencies… and paste https://github.com/bithuman-product/homebrew-bithuman.git. In a Package.swift:

.package(url: "https://github.com/bithuman-product/homebrew-bithuman.git", from: "2.19.0")
// then attach the product your target uses:
//   .product(name: "Expression2", package: "homebrew-bithuman")
//   .product(name: "Essence2Kit", package: "homebrew-bithuman")
//   .product(name: "Essence2", package: "homebrew-bithuman")

The products:

ProductImportWhat it isDeployment target
Expression2import Expression2the Expression 2 engine with a Swift APIiOS 16 · macOS 13
Essence2Kitimport Essence2Kitthe Essence 2 engine with a Swift API; it includes Essence2iOS 26 · macOS 26
Essence2import Essence2the Essence 2 engine as a C library, for C, C++ and pluginsiOS 26 · macOS 26

Every product ships ios-arm64, ios-arm64-simulator (arm64 only) and macos-arm64. bitHumanKit 2.4.0 is legacy and frozen; new apps use Expression2 or Essence2Kit.

Authenticate

The engines check an API secret when a session starts. Set BITHUMAN_API_SECRET in the scheme's environment, or pass it in code before you create an engine: Expression2Credential.set(secret) or Essence2Credential.set(secret) (Your API secret).

The scheme's environment is for local builds. Every copy of a shipped app carries its secret, so treat it as exposed: fetch it from your backend at startup, keep it in the Keychain, give each app its own secret, and rotate it if usage looks wrong (What a shipped app holds).

Credits pay for active session time, talking or idle, billed to the second (pricing).

A Mac app built from Xcode’s App template turns on App Sandbox. Under Signing & Capabilities → App Sandbox, tick Outgoing Connections (Client), or the engines cannot check your secret.

First frame

The macOS Expression 2 example is one Swift file. Clone it, fetch the sample avatar, and run it:

git clone https://github.com/bithuman-product/bithuman-examples.git
cd bithuman-examples/swift/macos-expression2
./setup.sh                                 # the wise-pup avatar, the shared engine and a speech clip
export BITHUMAN_API_SECRET="<your API secret>"
swift run -c release MacOSExpression2
Expected result
engine ready: 416x720, isReady=true
audio: 325451 samples, 20.34 s
generated 407 frames in … s -> out/first-frame.png

407 frames for 20.34 seconds of audio: one frame per 50 ms of speech. The first run prepares the engine for your Mac; keep Model/staged/ and later runs start faster.

The core of Sources/main.swift:

// excerpt: swift/macos-expression2/Sources/main.swift
let engine = try Expression2Engine.create(
    avatarContainer: model.appendingPathComponent("agent.imx"),
    sharedEngineContainer: model.appendingPathComponent("shared-engine.imx"),
    stagingDir: model.appendingPathComponent("staged"))
// …
let started = Date()
engine.feed(samples)
engine.flushTail()
// …
var frames = 0, idleTicks = 0
while idleTicks < 100 {
    var got = false
    while let (frame, _) = engine.pull() {
        if frames == 0 {
            writePNG(frame, width: engine.width, height: engine.height,
                     to: out.appendingPathComponent("first-frame.png"))
        }
        frames += 1
        got = true
    }
    if got { idleTicks = 0 } else { idleTicks += 1; usleep(50_000) }
}

Complete example

macOS Expression 2 walks through the tool above: requirements, your own avatar and audio, and troubleshooting. For a window with a microphone button, the iOS Expression 2 example is the same engine in a SwiftUI app.

Integrate into your app

The Swift API is the same on the Mac as on iPhone and iPad: feed 16 kHz mono audio, take frames on your player’s clock, end and interrupt replies. The whole table is on iOS & iPadOS; every entry point is on the Swift reference.

On a Mac:

  • Files: add the .imx files and engine resources to the app bundle. A sandboxed app reads only its bundle and container.
  • Quitting: call Essence2Engine.quiesceAll() from applicationWillTerminate.

Platform notes

  • Also on a Mac: the CLI renders an avatar or runs a live conversation with no code, and Python renders frames from your own scripts. Both run on Apple silicon.
  • Intel Macs are not supported.

Performance

ConfigurationEssence 2Expression 2
Apple M4 macOS · Swift package
4.8× real timeApple M4 · Swift package 2.15.0 · measured 2026-09-24
8.8× real timeApple M4 · Swift package 2.15.0 · measured 2026-09-24
Apple M4 macOS · CLI
4.2× real timeApple M4 · CLI 2.8.1 · measured 2026-09-27
8.4× real timeApple M4 · CLI 2.8.1 · measured 2026-09-27
Apple M4 macOS · Python
6.9× real timeApple M4 · bithuman 2.11.12 · measured 2026-09-25
8.4× real timeApple M4 · bithuman 2.11.12 · measured 2026-09-25

Times real time: seconds of avatar video rendered per second. At 1.0× or more, an avatar holds a live conversation. Select a figure for its release and date. All configurations and how we measure.

Troubleshooting

SymptomCauseFix
refusing to serve: no API secret was foundno secret in this shell or schemeexport BITHUMAN_API_SECRET=…, or set it in the scheme
cannot reach bitHuman to verify your credential in a Mac appApp Sandbox blocks outgoing connectionstick Outgoing Connections (Client) under App Sandbox
create throws before engine readythe model files are missingrun ./setup.sh from the example folder, so Model/ holds the three files
The link step prints about ten unable to open object file warnings naming a folder on another machinedebug paths recorded in the published binaryharmless; the tool runs
The first run is slowthe engine is prepared for this Mac oncekeep Model/staged/ between runs

Reference