LTX 2.5 22B distilled β€” 4-bit weights, a real 10-second video with its audio, and the arm that beats it

A asym_w4a8_int8 build of ltx-2.5-22b-distilled-transformer, measured on a complete 10-second video and its soundtrack against the BF16 original and against Lightricks' own INT8.

39.13 GiB β†’ 11.66 GiB, 3.36x lighter, and 1.95x faster than the original on the same 249-frame render. It is also 1.90x less faithful in the picture and 2.9x less faithful in the audio than Lightricks' own INT8 build β€” those numbers are here because they are the ones that decide whether you want this file.

Method, tools and the full measurement log: https://github.com/JoaoZaokk/comfy-quant-bench


Correction, 2026-09-13: the first version of this card measured half of what the model makes

LTX 2.5 generates video and audio in one latent. The first version of this card decoded only the video branch, and said so in its "not covered" list as if that were a footnote. It is not: an audio-video model judged on frames alone is judged on half its output. All three arms were re-rendered with the audio branch decoded (LTXVAudioVAEDecode on the second output of LTXVSeparateAVLatent), and the soundtrack is measured below with the same seed and the same three transformers.

The re-render also answered a question nobody had asked: the frames came back pixel-identical to the first run in all three arms (mean absolute difference 0.0 on frames 1/63/125/187/249), across a server restart and, for the BF16 arm, a different disk. The video numbers below are the first run's, reproduced exactly; the audio numbers are new.

The measurement: one 10-second video, three transformers, picture and sound

249 frames at 25 fps (9.96 s β€” LTX requires 8n+1, so 250 is illegal and 249 is the legal neighbour), 512Γ—512, 3 steps, cfg 1.0, euler, manual sigmas 0.909375, 0.725, 0.421875, 0.0, seed 1234, one RTX 3090. Audio: 48 kHz stereo, 9.93 s, decoded by the same audio VAE in every arm.

The transformer is the only thing that changes. All three arms use the same encoder (gemma4-12b-with-proj-ltx-2.5-comfy-int8-convrot), the same video VAE, the same audio VAE, the same prompt, the same seed and the same sigmas.

Picture

arm GiB whole run, 249 frames run s/frame MAE vs BF16 PSNR SSIM
BF16 original 39.13 780.7 s 3.14 β€” β€” β€”
comfy-int8-convrot (Lightricks) 20.03 481.5 s 1.93 4.10 29.71 dB 0.941
int8_tensorwise ours, no rotation (int8_ours/, added 2026-09-14) 20.03 493.9 s 1.98 8.23 25.15 dB 0.889
int8_tensorwise + ConvRot ours (int8_ours/, added 2026-09-14) 20.03 481.6 s 1.93 4.19 30.18 dB 0.944
this file, W4A8 11.66 400.9 s 1.61 7.81 25.39 dB 0.895

MAE is the mean absolute per-pixel difference on the 0–255 scale, averaged over all 249 frames; the spread is tight (ours 7.16–8.84, theirs 3.61–4.61). Motion energy β€” mean |frame_t βˆ’ frame_tβˆ’1| β€” is 1.73 for BF16, 1.78 for INT8, 1.71 for ours: nobody froze and nobody jittered.

The two time columns are the wall-clock of the whole run, not generation speed (corrected 2026-09-14; the header used to say "s/frame" with no qualifier). They include loading the transformer (39 / 20 / 11.66 GiB) and the 22.7 GB text encoder from a network share, the encode, the 3 sampling steps, both VAE decodes and the muxing. The sampler's own 3 steps take about 25 s (7.9–8.5 s/it, read from the server's progress bar) in the two 249-frame runs whose bars survived in the logs; which arm each of those was is not recoverable from the logs, so this card makes no speed claim between the arms. The 2.3 sibling card reports the sampler's time separately from the run's.

Sound

arm log-mel L1 vs BF16 SNR spectral convergence lag level
comfy-int8-convrot (Lightricks) 0.041 11.2 dB 0.124 0.0 ms βˆ’38.8 dBFS
this file, W4A8 0.120 3.4 dB 0.311 0.0 ms βˆ’38.3 dBFS
int8_tensorwise ours, no rotation (added 2026-09-14) 0.060 9.0 dB 0.166 0.0 ms βˆ’38.6 dBFS
int8_tensorwise + ConvRot ours (added 2026-09-14) 0.043 11.6 dB 0.119 0.0 ms βˆ’38.7 dBFS
control: silence 6.980 0.0 dB β€” β€” β€”
control: white noise at the reference's RMS 1.471 βˆ’3.0 dB 1.097 β€” β€”

The reference soundtrack is ambient β€” surf, wind, birds β€” at βˆ’38.7 dBFS with 0 % silent windows. Log-mel L1 is the mean distance between 64-band log-mel spectrograms and is the number closest to "sounds alike"; SNR is on the raw waveform and is phase-sensitive, which is why the cross-correlation lag sits beside it (0.0 ms in both arms β€” nothing slid in time). The two controls give the scale: silence scores 6.98, white noise at the same loudness scores 1.47. Both arms are far inside that β€” INT8 is 36x closer to the original than white noise, ours 12x. Neither arm changed the level (within 0.4 dB) and neither went mute.

The audio ranks the arms the same way the picture does, and by a wider margin: ours is 1.9x further from the original in the frames and 2.9x further in the soundtrack. On this bench that is the fifth family in a row where the INT8 arm is the more faithful one β€” and here the INT8 arm is Lightricks' own.

three arms, picture and sound

Frames 1 / 63 / 125 / 187 / 249, then each arm's log-mel spectrogram and waveform on a shared scale. The middle row tracks the top row closely β€” same thin white lighthouse, same rock, same framing. The bottom row is a different composition: a larger, closer, brick-coloured tower with dark smoke where the original has a light beam. All three are good videos with coherent motion across the full ten seconds and a soundtrack that belongs to them. Ours is a good video that is further from the original, in both senses. If you want the original's picture and sound, take Lightricks' INT8 and pay 8.4 GiB and 20 % more time for it. If VRAM is what binds you, this file gets a real ten seconds of video and audio out of a 24 GB card faster than anything else here.

Two more int8 builds, added 2026-09-14: the rotation is the recipe

Two int8_tensorwise builds of the same BF16 were converted on this bench on 2026-08-17 with tools/quant_int8.py (1440 layers each, 20.03 GiB, sidecars beside the weights in int8_ours/) and sat unmeasured until the closing round; the criterion was written before the numbers (bench/criterio_fechamento_2026-09-14.md, C, in the method repo). Same protocol as the tables above, same BF16 reference, live int8 encoder in every arm; contact sheet and JSON in int8_ours/.

  • With ConvRot, our int8 lands on Lightricks' int8. 4.19 against 4.10 in the frames (per-frame ranges 3.64–4.71 and 3.61–4.61), 0.043 against 0.041 in log-mel, SNR 11.6 against 11.2 dB. The same recipe β€” int8_tensorwise plus the ConvRot rotation, which is what comfy-int8-convrot is β€” reproduces the same result from the same BF16.
  • Without the rotation, the same 8-bit weights and 8-bit activations land 2x farther in the frames (8.23) and 1.4x in the sound (0.060). In the frames that is farther than this W4A8 file (7.81), which keeps the rotation and drops the weight to 4 bits; in the sound the unrotated int8 is still 2x closer than W4A8. A per-tensor int8 scale over an 8192-wide activation with outliers is a coarse scale, and the rotation is what makes it acceptable. On the contact sheet the two rotated int8 arms keep the BF16's composition; the unrotated one draws the lighthouse taller, closer and redder β€” the same drift this W4A8 makes. The criterion had put both int8 builds inside 3–6 MAE; the unrotated one refuted it, and the refutation's own reading ("worse than 4 bits = broken build") does not hold: same scene, SSIM 0.889 against W4A8's 0.895, ranges overlapping.
  • Sampler time from the server's progress bar (3 steps, 249 frames, 512 px, RTX 3090): BF16 8.48 s/it (partial load, 25 s), int8 ours 2.02 s/it, int8 + ConvRot ours 2.12 s/it, Lightricks' int8 2.14 s/it, this W4A8 file 2.26 s/it β€” single runs each, so the four quantized arms sit within 12 % of each other and this W4A8 is not faster per step than int8 at this size; what it buys is the 11.66 GiB against 20.03. The "whole run" column is wall-clock that includes loading a 20 GB transformer and the 20 GB encoder over the network on every run β€” 6 s of sampling inside 400–850 s runs β€” which is why it ranks the arms by disk and not by transformer.
video + audio (MP4, h264 + AAC) lossless audio (FLAC)
int8 ours, no rotation int8_ours/int8_no_rotation.mp4 int8_ours/int8_no_rotation.flac
int8 + ConvRot ours int8_ours/int8_convrot.mp4 int8_ours/int8_convrot.flac

Listen for yourself

The proofs ship in this repo, not in a description of them:

video + audio (MP4, h264 + AAC) lossless audio (FLAC)
BF16 original av/bf16_original.mp4 av/bf16_original.flac
INT8, Lightricks av/int8_lightricks.mp4 av/int8_lightricks.flac
W4A8, this file av/w4a8_this_file.mp4 av/w4a8_this_file.flac

Every number above was computed on the lossless PNG frames and FLAC files, never on the MP4s; av/comparacao_av.json carries all of them, per arm, including the controls.

The file

file bytes GiB layout
ltx-2.5-22b-distilled-transformer-w4a8.safetensors 12,520,267,816 11.66 1440 Γ— asym_w4a8_int8, group_size 16, convrot_groupsize 256
int8_ours/ltx-2.5-22b-distilled-transformer-bf16_int8.safetensors 21,503,879,904 20.03 1440 Γ— int8_tensorwise, no rotation β€” 2x farther from BF16 than the rotated one; kept as the measured negative
int8_ours/ltx-2.5-22b-distilled-transformer-bf16_int8_convrot.safetensors 21,503,941,832 20.03 1440 Γ— int8_tensorwise + ConvRot, convrot_groupsize 256 β€” indistinguishable from Lightricks' int8 on this measurement

Source: ltx-2.5-22b-distilled-transformer-bf16.safetensors, 42,018,190,584 B, byte-for-byte the file Lightricks/LTX-2.5 publishes. Backend recorded in the sidecar: comfy_kitchen.backends.cuda.

Running it

Needs --disable-dynamic-vram. At 11.66 GiB it is resident on a 24 GB card and needs no block splitting β€” which is most of why it is faster than the other two arms, both of which have to move weights every step.

For contrast, and because it is a measured limit rather than a guess: the BF16 original does not render this video without help, and one way of helping it kills the server. Five attempts:

BF16, no DisTorch2,   49 frames   -> CUDA error: out of memory (from mem_get_info; the card is exhausted)
BF16, DisTorch2 6 GiB from cuda:1, 49 frames  -> works, 717.8 s
BF16, DisTorch2 6 GiB from cuda:1, 249 frames -> Windows fatal exception: access violation, process dead
BF16, DisTorch2 40 GB on cpu,      249 frames, file on an SMB share, 24 GiB RAM free
                                              -> access violation inside torch/storage.py __getitem__
                                                 while load_torch_file memory-maps the 39 GiB file;
                                                 the ComfyUI process died with it
BF16, DisTorch2 40 GB on cpu,      249 frames, same bytes under a second name on ANOTHER SMB
                                              share (W:), 40 GiB RAM free
                                              -> works, 769.6 s, and pixel-identical to the first run

Correction, 2026-09-13 (later the same day). The first version of this list called W: "a local NTFS disk". It is not: net use lists it as \\192.168.3.40\zfe, a network share like D: and P:. So both BF16 loads were memory-maps over SMB β€” one died, one survived β€” and the LTX 2.3 work that followed added three more deaths with the same signature (access violation inside torch/storage.py __getitem__ while load_torch_file pages the mapped file), on 39–43 GiB files, with 32–44 GiB of RAM free, while a bare Python process paged the same 39 GiB file through in four seconds. The redirector was the suspect for a few hours, as a judgement; the next paragraph is the measurement that replaced it.

And the redirector was the wrong suspect too β€” measured 2026-09-14. The same two-view mapping that safe_open performs, done by hand in a bare Python process without ComfyUI, dies the same way on the local C: drive as on W:. What was measured on this 39.13 GiB file, with the system commit counters read before and after each call (GetPerformanceInfo):

call                                                       commit charge
safetensors.safe_open(framework="pt")                      +40.8 GiB at open, before any tensor is read
   (two copy-on-write views of the same file: memmap2 for the header and
    torch.UntypedStorage.from_file(shared=False) for the data; +80.2 GiB while both are alive)
torch.empty of the model's parameters                      +40.7 GiB more
read-only mmap (what numpy and the GGUF loader use)          0
UntypedStorage.from_file(shared=True)                        0

Windows charges a copy-on-write view its whole size at mapping time, so ComfyUI's normal safetensors path needs twice the file in commit just to open it and three times to build the model. This machine's commit limit is 124.8 GiB (63.6 GiB of RAM plus a 61 GiB system-managed pagefile) with about 70 GiB already committed by other processes. When the charge forces the pagefile to grow, the new view sometimes comes back with the limit raised but the charge not taken, and the first read through it is an access violation in torch/storage.py __getitem__ β€” the server's exact signature (safe_open + first get_tensor on W:, 3 of 3 runs; the hand-made two-view mapping on C:, 1 of 1; the same calls survive on other runs, which is why one BF16 load in seven succeeded). The two deaths inside nn.Linear.__init__ β€” the model's torch.empty β€” carry the same signature and were not reproduced in isolation. So the list above stays, with a different reading: rows 3 to 5 died of commit, not of the network. The LTX 2.3 reference was rendered without the safetensors reader at all: the same BF16 weights, bit for bit, in a GGUF container read through a read-only memmap (tools/safetensors_to_gguf_bf16.py; bytes, dequantized weight and Linear output verified identical on 12 sampled layers with tools/probe_gguf_bf16_equivalence.py).

What is NOT covered

  • One prompt, one seed, one resolution (512), one frame rate, one card. Three arms, not a sweep.
  • MAE, PSNR, SSIM and log-mel distance are distances from the reference, not judgements of quality. They say this build lands further from the original; they do not say a viewer or a listener prefers the original. Watch and listen to the files.
  • No audio metric here was validated against human judgement on this bench, and audio-video synchrony is not measured β€” each branch is compared with its own counterpart.
  • The text encoder is quantized in every arm (comfy-int8-convrot), held fixed so the axis stays clean. A BF16-encoder run would measure a different thing and is not included.
  • No per-layer error analysis for this checkpoint. Unlike the image models on this bench, this build predates the calibration record, so its sidecar has no source_identity_sha256 and there is no err_w4a8 distribution to quote.
  • convrot_groupsize 256 only.
  • The two int8 builds were measured once each, one seed, one prompt; their per-step times come from a single run's progress bar. The BF16 per-step (8.48 s/it) is from the one server log that survived the 2026-09-13 renders; Lightricks' int8 and this file were re-rendered on 2026-09-14 for their bars alone (pixels identical to the first run, as before).

License and changes

LTX 2.5 is distributed under the LTX-2.x Community License Agreement, and this derivative is distributed exclusively under those same terms, as Section 3.2 requires. The complete agreement, including the use restrictions in Section 4 and Attachment A, ships beside these weights as LICENSE_LTX_2x_COMMUNITY.txt β€” read it before use; its acceptable-use restrictions bind you and anyone you pass this on to.

Statement of changes, per Section 3.3: the 1440 Linear layers of the transformer were re-encoded from BF16 into ComfyUI's asym_w4a8_int8 format (4-bit weights, 8-bit activation path, Hadamard rotation at group size 256). No weights were fine-tuned, no architecture was altered, and every non-Linear tensor is preserved from the source.

Credits

  • Lightricks β€” LTX 2.5 itself, the BF16 weights this was converted from, and the comfy-int8-convrot build used as the comparison arm. That arm is more faithful than this one, in picture and in sound, and it is theirs.
  • ComfyUI / comfy-kitchen β€” the asym_w4a8_int8 format, the MixedPrecisionOps dispatch and the CUDA kernels that execute it.
  • ComfyUI-MultiGPU (DisTorch2) β€” without its block splitting the BF16 reference arm could not have been rendered at all on this hardware, and there would have been nothing to compare against.
Downloads last month
142,479
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support

Model tree for JoaoZaokk/LTX-2.5-22B-distilled-W4A8-ConvRot

Quantized
(36)
this model