FaceFusion Mobile model sets
Converted models for FaceFusion Mobile, an Android port of FaceFusion's default face-swap path that runs entirely on the phone.
There are two sets here, and the app picks one for you at startup:
- Hexagon context binaries, four tiers, for phones with a Qualcomm NPU. These are compiled QAIRT graphs, not ONNX, and each one is pinned to a Hexagon architecture. A binary will not load on anything else.
- ncnn models, one set, for every other phone. These run on the GPU through Vulkan with a CPU fallback, and they are what the app downloads when there is no Hexagon NPU to talk to.
Converted with QAIRT 2.49.0 (W8A16 per-channel quantisation) and ncnn (fp16 storage).
Which files do I need
The app measures the chip at startup and downloads one set. You do not choose, and you must not mix tiers.
| set | download | for |
|---|---|---|
v68 |
360.1 MB | Snapdragon 888 and older, 8 Gen 1, or any part with under 8 MB VTCM |
v73 |
360.6 MB | 8 Gen 2, 8 Gen 3, and v79 parts other than the SM8750 |
v79 |
361.1 MB | Snapdragon 8 Elite (SM8750) |
v81 |
355.4 MB | Snapdragon 8 Elite Gen 5 |
ncnn/ |
604.1 MB | everything else, including non-Qualcomm phones |
The ncnn set is roughly twice the size because it is stored at fp16 rather than quantised to 8 bits.
⚠ Context tier is not the same thing as runtime architecture. An 8 Gen 3 is v73 silicon that takes the v68 context tier but still needs the V73 runtime, which ships inside the APK.
manifest.json lists every file with its size and SHA256. Verify after downloading. A
truncated context binary is a plausible looking file that fails at load, and the app checks
each hash before it uses anything.
What is in a tier
| file | what | notes |
|---|---|---|
yoloface_<tier>.bin |
face detector, 640x640 | |
fan2d_<tier>.bin |
2D face landmarker | graph cut at heatmaps |
arcface_<tier>.bin |
face recogniser | 512-d embedding |
hyperswap_<tier>.bin |
face swapper, 256x256 | fp32-demoted before quantisation |
gpen_<tier>.bin |
face enhancer, 256x256 | optional, see below |
wav2lip_<tier>.bin |
lip syncer, 96x96 | optional, needs a clip with audio |
nsfw_v79.bin, nsfwq_<tier>.bin, nsfwq2_<tier>.bin |
content checker | which one depends on the tier |
The content checker
Mandatory. The app treats a missing gate context as an initialisation failure, not as a reason to carry on unchecked.
Three filenames cover it because the build depends on the architecture and because one older app version has to keep working:
nsfw_v79.binis fp32, and fp32 only builds for v79.nsfwq2_<tier>.binis the quantised build, calibrated on the input range FaceFusion 3.8.2 actually uses. This is the current one, asked for by app 0.2.2 and later.nsfwq_<tier>.binis the superseded build, calibrated on the wrong input range. It stays hosted only so that installs of app 0.2.1 keep finding a gate. Nothing newer asks for it.
The quantised build shifts the decision statistic about +0.087 toward flagging, so the lower tiers refuse marginally more readily than v79 does.
The face enhancer
gpen_<tier>.bin is the one file the app will run without. It restores detail in the
swapped face at 8.57 GMAC per face, against the swapper's 31.93, and the pipeline skips the
stage when the binary is absent. An install that predates it keeps working and can fetch
just this file from Settings instead of downloading the set again.
The current build is 2.45 ms per face on v79. An earlier one was 23.35 ms, which more than doubled the frame time. Collapsing 18 blur blocks into a grouped convolution accounts for the difference, with no change in accuracy.
The lip syncer
wav2lip_<tier>.bin redraws the mouth to match speech. It is optional and it is the one
model here that needs something other than pictures: it takes a 200 ms window of the clip's
own audio, as an 80x16 mel spectrogram, alongside the face crop with its lower half blanked
out, and draws a mouth that fits the sound. A photo or a silent clip has nothing to
condition on, so the app will not offer it for one.
W8A16, 43.7 MB per tier, and it quantises better than anything else in this repository:
| deploy SNR | 44.30 dB worst of 6 held-out (float build: 47.61) |
| latency | 1.03 ms on v79 |
| first load | 13 ms |
For comparison the swapper measures 30.87 dB and the enhancer 38.23. A 143-node graph of nothing but convolutions, ReLU and adds is simply an easier thing to quantise.
The model costs about 1 ms per frame. Preparing the audio costs about 28 ms per second of audio, once per clip, and that is the part worth knowing about.
⚠ It runs on the audio as recorded. Upstream takes the mel of the ISOLATED voice, run through a separate 66.8 MB voice extractor first. That model is not converted here, so a clip with a music bed under the speech is fed something upstream would not have fed it. On clean speech the two paths agree.
The non-Qualcomm set
ncnn/ holds .param and .bin pairs for the same six models. App 0.4.0 and later resolve
it when the phone has no usable Hexagon NPU. On a Snapdragon 8 Elite with the NPU switched
off it runs about 325 ms per 720p frame against the NPU's 75 ms, and produces the same swap
rather than an approximation of one, agreeing to 42.7 dB.
⚠ Two stages are pinned to the CPU whatever the phone has, because Vulkan gets them measurably wrong: the face enhancer, and the content checker. The checker matters more than the enhancer does, because on Vulkan it errs toward allowing.
Output resolution
The swapper is 256x256, but output is not fixed at 256. Pixel boost warps the face larger and runs the same binary over N-squared polyphase sub-images, so 512, 768 and 1024 come out of these same files with no reconversion.
What has actually been run
Every accuracy, latency and correctness number in this repository was measured on an SM8750
(Galaxy S25 Ultra), which is the v79 tier.
v81: two of the six graphs,fan2d_v81andnsfwq_v81, have been profiled on a real Galaxy S26 Ultra through Qualcomm AI Hub. Both load and execute correctly, faster than v79, with every layer on the NPU. The other four were not uploaded, because their licences do not allow it, so they have not run on v81 silicon.v68andv73: built, and their tier mapping is asserted against a device table, but neither has had meaningful time on hardware of its own generation.ncnn/: only ever run on a Snapdragon with the NPU disabled, which is not the same thing as running on a phone that has no NPU.
If you run one of these, the app's Settings, Share bug report is the most useful thing you can send back.
Licences, read before redistributing
These are derived works of FaceFusion's model weights and carry the same terms. They are not uniformly permissive.
| model | licence |
|---|---|
yoloface_8n |
GPL-3.0 |
arcface_w600k_r50 |
Non-Commercial |
inswapper_128 |
Non-Commercial |
hyperswap_1a_256 |
ResearchRAIL |
gpen_bfr_256 |
Non-Commercial (vendor yangxy, per upstream's own model metadata) |
wav2lip_gan_96 |
Non-Commercial (vendor Rudrabha, per upstream's own model metadata) |
2dfan4 |
MIT |
nsfw_2 |
Apache-2.0 (vendor Marqo) |
The original weights are distributed by the FaceFusion project itself, from
huggingface.co/facefusion/models-* and github.com/facefusion/facefusion-assets. This
repository re-hosts converted forms of them for Android. Upstream's own licence is
OpenRAIL-AS, which carries use restrictions. Read
https://github.com/facefusion/facefusion/blob/master/LICENSE.md before you use, modify or
redistribute any of this.
Intended use
Offline, on-device face swapping. The app that consumes these files ships FaceFusion's content checker as a blocking check and refuses material it flags, with no output file.
Do not use this to create images or video of real people without their consent. That is the principal way software of this kind causes harm, and it is what the content checker and the OpenRAIL-AS use restrictions exist to limit.