Skip to content

Releases: OuincheWinch/DiffusionBear

v0.3.7 — beta

v0.3.7 — beta Pre-release
Pre-release

Choose a tag to compare

@OuincheWinch OuincheWinch released this 08 Oct 14:42

Tested on the owner's own machine, from the installed build, before publication.

Phases 1-5: Full optimization pipeline shipped

TeaCache step-skipping (Phase 1)

4-21% speedup on FLUX.2, Krea2, Z-Image. Auto-disabled for FLUX.2 img2img where KV cache applies.

Disk-backed prompt cache (Phase 2)

SQLite + .npy serialization — survives restarts, ~9% cold-start improvement.

Krea2 TE q4@load (Phase 3)

Eliminates 7.5 GB bf16 spike, saves ~52 s cold start. Removed 150+ lines of disk-cache/WeightApplier patch logic.

mx.compile + 2-step warmup (Phase 4)

11-20% speedup after 2 warmup steps on FLUX.2, Krea2, Z-Image.

Quantization sweep (Phase 5)

q4 confirmed optimal; q3/q2 catastrophic quality loss on FLUX.2/Krea2 (PSNR ~10dB). Z-Image ignores quant param.

Krea2: 8 steps now default

4-step + distillation LoRA = 8 steps compute equivalent. 8-step is now default for quality.

Arena Benchmark: 40/40 scenes @ 768×512

Model Steps TeaCache Avg Time
FLUX.2-klein 4B 4 0.15 43s
FLUX.2-klein 9B 4 0.15 95s
Krea2 Turbo 4 0.20 256s
Z-Image Turbo 8 0.12 152s

Assets verified byte-for-byte against local.

Checksums

ALL_SHA256SUMS published beside assets. Verify with shasum -a 256 DiffusionBear-0.3.7-arm64.zip.

v0.3.6 — beta

v0.3.6 — beta Pre-release
Pre-release

Choose a tag to compare

@OuincheWinch OuincheWinch released this 07 Oct 17:27

Tested on the owner's own machine, from the installed build, before publication.

Steps are clamped end to end, and the preset chips are gone

A guidance-distilled model clamps its step count in the engine — Juggernaut XL
Lightning runs 12 — so a 20-step request ran 12 while the UI counted towards 20.
/api/models now exposes max_steps, the slider tops out there under a
"Distilled — max N steps" tag, and the progress readout, the ETA and the run all
agree. The one-click preset chips that sat between the model picker and Generate
are removed: they duplicated the size picker and the now-capped slider, and their
labels came from the registry untranslated.

Half-downloaded models no longer read as INSTALLED

The installed-status probe ignored .incomplete/.part files under .cache/,
which is exactly where huggingface_hub stages local_dir downloads — so an
unfinished transfer showed no Download button, answered already_installed to
the API, and generation then correctly refused it. Both probes count every
partial now, and a killed run's orphaned partial is dropped instead of failing
the installer's layout check forever.

Engine tracebacks stay in the log

A failed generation no longer pastes a forty-line SDXL traceback into the UI.
The job error keeps the first actionable line and the generation reference; the
daemon's stderr stays in the diagnostics log. Hugging Face non-checkpoint names
are matched on token boundaries now, so exploration, floral and brave no
longer read as lora/vae.

Checksums

ALL_SHA256SUMS is published beside these assets. Verify with
shasum -a 256 DiffusionBear-0.3.6-arm64.zip and compare against it.

v0.3.5 — beta

v0.3.5 — beta Pre-release
Pre-release

Choose a tag to compare

@OuincheWinch OuincheWinch released this 05 Oct 21:13

Tested on the owner's own machine, from the installed build, before publication.

Fast VAE decode was dead in every previous build

Turning on fast VAE appeared to do nothing, and the reason was that TAESD never ran. taesd_mlx.py resolved its weights from __file__/data, which inside the signed bundle is inside the signature, so the lookup missed and every SDXL render quietly fell back to the full VAE inside an except that printed one line to stderr nobody reads. It now resolves through the model store, so it follows store_path too.

Verified in the installed app: a 128×128×4 latent decodes to 1024×1024×3, and a real generation logs zero TAESD warnings. The owner reports the speed difference is immediately obvious.

The bundle named the build machine in 88 files

82 console-script shebangs, both pyvenv.cfg home lines, six venv activate scripts, a PEP 610 direct_url.json and three docstrings all carried the build machine's volume path. It worked on the machine that built it, which is exactly why it was never noticed — and it would have failed on every other Mac.

Fixed by construction rather than by computing a better absolute path, because there is no correct absolute path for something that can be dragged anywhere. pyvenv.cfg now carries a relative home matching the existing relative symlinks, and each console script becomes a #!/bin/sh trampoline that finds its own interpreter:

'''exec' "$(dirname "$0")/python" "$0" "$@" # '''

The build now refuses to ship on an absolute shebang, an absolute home, or a build path anywhere under bin/. It then rehearses the relocation: copies the finished bundle to another path and asserts both interpreters report a base_prefix under the new location, that mlx and torch import, and that a trampolined script executes. That rehearsal earned its keep immediately — it caught a /var vs /private/var comparison bug in the check itself, and a metadata scrub that had been left running after signing, which invalidated the seal.

The build installs itself now

Both hand-rolled copies failed silently. cp -R produced a bundle 1,973 files short and reported success; ditto on APFS-over-USB exits 0, copies nothing, and merges into an existing bundle, leaving a hybrid with a broken seal and a stale Info.plist. INSTALL_APP=1 ./build_app.sh removes the target, copies, then proves the result by comparing file counts and re-verifying the signature — a mismatch is a loud failure rather than a broken app found later.

Size selection

The size control multiplied a base resolution by a ratio, which produced sizes no model was trained on — 16:9 at base 1024 is 1024×576, and it looks it. It is now a model-aware picker: four orientation tabs over exact native resolutions, with Custom for anything else, and Custom reports the true ratio of what you typed. Sizes below 768 px are offered by default; a filter that hid them was added and then removed, because it cost a line of height to hide exactly one button, did nothing on two of the three tabs, and silently resized the image when ticked.

SDXL models are checked before you wait 2 minutes to find out

The old validation searched recursively for any .safetensors anywhere, so a single-file checkpoint — how almost every SDXL model is published on Hugging Face — passed and then failed at generation with a forty-line traceback ending in No .safetensors files in .../unet. An SDXL engine now requires the diffusers layout at adopt time and names the missing part.

SDXL: discovery and loading, fixed

Reported from a second machine where Juggernaut XL Lightning would not load.

Every SDXL model read "Not runnable here". The table that decides which
architectures this app can execute covered FLUX.2, Z-Image, Krea and Qwen — and
had no SDXL entry at all. So even RunDiffusion/Juggernaut-XL-Lightning, the
registry's own repository, was listed as unusable while showing an INSTALLED badge
on the same row. Four SDXL engines are now recognised.

A ControlNet is no longer offered as a checkpoint. Name matching alone is not
safe: juggernaut-xl-lightning-4step-controlnet-coreml-6b contains "juggernaut",
"xl" and "lightning". It would have cost a multi-gigabyte download to reach a
dead end, so ControlNets, LoRAs, VAEs and upscalers are rejected up front.

A failed load now says what to do. It previously produced a forty-line
traceback ending in No .safetensors files in .../unet. The four possible causes
get four different messages, because they need different fixes:

cause what you are told
interrupted download "unfinished download (N MB still partial) — re-download"
single-file checkpoint "needs the diffusers folder layout — convert it"
empty unet/ "the download is incomplete"
not installed "does not exist, so the model was never installed"

A download is no longer marked installed unless it worked. The task was
declared successful on the strength of the transfer loop finishing, without ever
looking at what had been written. That is how an interrupted 9.5 GB shard left an
empty unet/ behind an INSTALLED badge. The layout is verified before the task is
reported done.

The app moves port instead of refusing to start

If something else already owns 127.0.0.1:8001, the app used to show an alert and
give up. 8001 is a popular port — a dev server, an older build, an unrelated app —
and none of those should stop this one from launching.

It now tries each rung of 8001, 9025, 10049, 11073 … 17217 (ten rungs, +1024)
and takes the first that is free. If a rung is already serving a DiffusionBear
backend
, that one is reused instead, so opening the app twice does not leave two
engines resident with two copies of the model in memory.

Telling our backend apart from someone else's matters here, and it is not as easy
as "something answered": a 404 from an unrelated server looks identical to a healthy
answer under that test. So the probe asks for /api/version and requires JSON that
names DiffusionBear and carries a version. A timeout, an empty body, an HTML error
page, or JSON for another service all mean "not ours, try the next rung" — and a
listener that accepts the connection but never replies is abandoned on a bounded
wait rather than hanging the launch.

No proprietary fonts

The app embeds no fonts — no @font-face, no web font in the bundle — so there
is nothing to redistribute and nothing to license.

The interface names fonts, as a native Mac app does: CSS keywords the browser
resolves (system-ui, ui-monospace), macOS system fonts macOS provides
(-apple-system, Menlo, SFMono-Regular), and open fallbacks (DejaVu Sans Mono, Liberation Mono — Bitstream Vera and SIL OFL 1.1).

An audit of 0.3.5 found Consolas in two stylesheets: a Microsoft font,
proprietary, and not present on a Mac without Office, so it could only ever
resolve on a machine that already had it. Removed. backend/test_font_licensing.py
now enforces this and is default-deny — any font name that is not a CSS
keyword, a macOS system font, or a known open font fails, because a denylist only
catches what someone already thought of.

The 38 TTF files inside venv/ are matplotlib's, arriving with a dependency no
backend module imports. All three families are free, verified against the licence
text that ships beside them: DejaVu (Bitstream Vera + public-domain changes), STIX
(SIL OFL 1.1) and Computer Modern (Knuth, public-domain lineage).

Also

The i18n catalogue parser demanded exactly two leading spaces while two files indent some entries four and one zero, so 23 translations were never actually checked. The parser now accepts two or more and the bound is exact zero instead of a documented 23.


Requirements unchanged: Apple Silicon, macOS 15 or later, right-click → Open on first launch because the app is ad-hoc signed and not notarised.

Verify a download with shasum -a 256 against ALL_SHA256SUMS. Model weights are not included; they download on first use from Hugging Face or Civitai into ~/Library/Application Support/DiffusionBear.

v0.3.4 — beta

v0.3.4 — beta Pre-release
Pre-release

Choose a tag to compare

@OuincheWinch OuincheWinch released this 04 Oct 15:59

Tested on a real machine before publishing.

The Hugging Face token was saved to one path and read from another. Saving wrote DATA_DIR/hf_token.txt; reading looked in the app bundle, a path nothing writes. So the token was stored correctly and never read back — the UI showed it unset after every relaunch, and gated downloads had nothing to authenticate with. Same fix Civitai already had.

An interrupted download could not be restarted. After a cancel or a failure the row kept its status and lost its download button. It now returns whenever nothing is in flight, labelled Retry, and resumes from the bytes that arrived. Broken on both the Generate page and the Models page, which share one component.

Unrecognised model directories can be adopted. The storage panel already flagged directories nothing references; it now proposes which model each resembles and can point that model at it, using the same override the downloader writes. The directory becomes selectable and stops being reported. Nothing is copied: adoption is a pointer, costs no disk, and unregistering undoes it. A directory it cannot identify confidently is left alone rather than guessed at.

The model list is ordered installed-first, then alphabetically, with adopted local models sorting by name among the rest instead of at the end. Applied server-side so the Generate dropdown, Models tab and defaults section cannot drift apart.

Also fixes a crash introduced during this work: a temporal dead zone where canStart read showBar before its declaration, so every render of the model installer — on both the Generate and Models pages — threw and the recovery boundary blanked the interface. Lint, 504 backend tests and CI were all green through it, because none of them evaluate a component.

Verified on a clean machine before release: app opens without the recovery screen, Models tab renders, a saved token survives a relaunch, a cancelled download can be retried, and a hand-placed model directory can be adopted.

Requirements are unchanged: Apple Silicon, macOS 15 or later, and a right-click → Open on first launch because the app is ad-hoc signed and not notarised.

Verify a download with shasum -a 256 against ALL_SHA256SUMS. Model weights are not included; they download on first use from Hugging Face or Civitai into ~/Library/Application Support/DiffusionBear.

v0.3.3 — SUPERSEDED, use v0.3.4

Pre-release

Choose a tag to compare

@OuincheWinch OuincheWinch released this 04 Oct 11:26

SUPERSEDED. Do not install.

v0.3.3 fixed the stalled download, but a Hugging Face token you saved was never read back, an interrupted download could not be restarted, and model directories you placed by hand could not be adopted. Use v0.3.4.

v0.3.2 — SUPERSEDED, use v0.3.3

Pre-release

Choose a tag to compare

@OuincheWinch OuincheWinch released this 04 Oct 07:47

SUPERSEDED. Do not install.

0.3.2 fixed the NameError in the download button, but on a fresh machine the transfer then stalled at 99% while downloading 22.1 GB of weights the app never loads. Use v0.3.3: 4.3 GB, and it completes in about 14 minutes.

v0.3.1 — SUPERSEDED, use v0.3.3

Pre-release

Choose a tag to compare

@OuincheWinch OuincheWinch released this 03 Oct 22:07

SUPERSEDED twice over. Do not install.

Model downloads were broken here: the button failed immediately for every mflux model with a NameError, so a fresh install could not obtain any model.

Use v0.3.3, which also fixes a stalled transfer that left a fresh install frozen at 99% while downloading 22.1 GB of weights the app never uses.

Public BETA v0.1.1 (beta)

Pre-release

Choose a tag to compare

@OuincheWinch OuincheWinch released this 23 Sep 21:23
122f07f

MLX-Diffusion : Yet another open source image generator on Apple Silicon

Public BETA
v0.1.1 (beta)