Open-source project
BinomialLLC/basis_universal avatar
BinomialLLC/basis_universal

Basis Universal: ship one texture file and let the GPU decide how to unpack it

Basis Universal GPU Texture Codec

3,113 stars345 forksC++Apache-2.0

At a glance

What is it?
Binomial's transcoding system for portable LDR and HDR GPU textures, with eight codecs that turn a single intermediate file into whatever compressed format the target hardware actually wants.
Who is it for?
Basis Universal solves a specific and annoying problem: the same game or application has to look the same on a desktop GPU using BC7, a phone using ETC2, and a browser using ASTC, and shipping all three sets of assets is expensive. Its answer is to encode once into a supercompressed intermediate and transcode on the fly at load time, which the README frames as making GPU textures infrastructure rather than a per-platform chore.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 36 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem: one asset, many hardware decoders

The README states the goal before describing the features, and the goal is the reason the project exists: to simplify the encoding and efficient distribution of portable LDR and HDR GPU texture, image and short animated texture content in a way that is compatible with any GPU, rendering or graphics API, or platform including plain WASM.

The tension being resolved is old and well understood. A GPU can only sample textures in the formats its hardware block decoder supports. BC and BC7 on desktop GPUs, ETC1 and ETC2 on most mobile, PVRTC on older Apple hardware, ASTC on anything modern enough, RGBA as the universal fallback that costs four bytes per pixel. An application targeting several of those needs several copies of every texture, encoded separately, and it needs to pick the right one at runtime based on a capability query it did not write.

Basis Universal inverts the order. You encode once into a supercompressed intermediate that is not tied to any GPU block format, ship that, and transcode on the fly into whatever the running device wants. The README calls the result rapid transcoding to virtually any compressed GPU texture format released over the past quarter century, and names the three container formats it can start from and end in: the KTX2 open standard from the Khronos Group, its own .basis format, and Microsoft's DDS.

The repository has 3,113 stars, 345 forks and 127 open issues, is Apache 2.0 licensed with the copyright line running 2016 to 2026, and was last pushed on 2026-09-01. The topic tags read as a summary of the field: compression, dds, gpu-texture-compression, jpeg, ktx, mipmapping, png, qoi and texture-compression.

Eight codecs, and they are not interchangeable

The heart of the README is a numbered list of eight modes and codecs, in the order they were implemented. Reading it is the fastest way to understand where the project has put its weight over a decade of work.

ETC1S comes first: a supercompressed subset of ETC1, designed for very fast transcoding to other LDR formats, described as low to medium quality but high compression, with transcoding slightly faster than libjpeg. UASTC LDR 4x4 follows, with or without RDO, a custom ASTC 4x4-like format for high quality at very fast transcoding speed. UASTC HDR 4x4 is standard ASTC HDR 4x4 data constrained for fast transcoding to BC6H, and ASTC HDR 6x6 with or without RDO is standard ASTC HDR 6x6. UASTC HDR 6x6 Intermediate, called GPU Photo HDR, is the supercompressed form of that.

Item six is ASTC LDR 4x4-12x12, covering all 14 standard ASTC block sizes with or without basic windowed RDO. Item seven is XUASTC LDR 4x4-12x12, also all 14 block sizes, called GPU Photo LDR or SDR: a latent-space codec supporting ASTC LDR supercompression with Weight Grid DCT for very high quality, extreme bitrate scalability, and optional adaptive deblocking that can run on the CPU or through a simple GPU pixel shader compatible with mipmapping and filtering, with three entropy coding profiles, Zstd, arithmetic, or hybrid. It is also referred to as JPEG for ASTC.

Item eight is XUBC7, a latent-space codec for standard BC7 covering all modes, all mode features, and all two and three subset partition patterns, supporting lossless or lossy supercompression with near-loss transcoding to ASTC LDR 4x4 and real-time encoding to ETC1/2 and PVRTC1 among others. Its numbers are given concretely: lossless bitrate around 3.5 to 5.6 bpp, lossy around 0.8 to 3.5 bpp, with a lossy sweet spot of roughly 1.15 to 2.5 bpp, fully fixed-point decompression and multithreaded transcoding up to 8 threads, always using Zstd.

So the catalogue is not eight ways of doing the same thing. ETC1S trades quality for speed and size, UASTC and XUASTC trade size for quality, and XUBC7 exists because BC7 is still the most common desktop format and you cannot ship BC7 to a phone.

Rate-distortion optimisation is the recurring dial

RDO appears beside six of the eight codecs, and it means rate-distortion optimisation: spending bits where they buy the most visible quality and spending fewer where they do not. For a texture pipeline this is the difference between a uniform compression setting and an encoder that understands that a dark leather texture can be crushed and a character's face cannot.

XUASTC takes this furthest in the current release. As of v2.50, the XUASTC and ASTC encoders support in-loop deblocking via a global optimisation step using Stochastic Coordinate Descent. An optional deblocking pixel shader can reduce block artefacts on the largest ASTC and XUASTC block sizes, 10x8 or larger by default, and the encoder can now optimise its output taking that filter into account. That is a genuinely subtle piece of engineering: the encoder no longer picks coefficients and then filters them, it optimises for what the filter will output.

The deblocking shader is worth calling out as an engineering choice rather than a feature. The README says it is compatible with mipmapping and filtering, which is the hard part, since a filter that breaks mip generation produces worse artefacts than no filter at all. The repository has two shader directories, `shader_deblocking_glfw/` and `shader_deblocking_d3d11/`, which is how you cover both a desktop GL context and Direct3D 11 without a rendering abstraction layer.

XUBC7 uses a different lever. It supports optional RDO, uses residual weight grid DPCM and in lossy mode absolute plus residual Weight Grid DCT, and the README is explicit that XUBC7 decompression is fully fixed-point, which is what makes it viable on hardware without fast floating point in the texture path.

Where the transcoding actually happens: native, WASM, WASI, Python

The deployment story is broader than most codec projects manage, and it is the part that decides whether you can use the thing.

The C and C++ encoder and transcoder libraries compile to native code or to WebAssembly for web or WASI. All encoder and transcoder features are reachable from JavaScript through a C++ wrapper library which optionally supports WASM multithreading for fast encoding in the browser. WASI builds are supported for the command line tool and for the encoder and transcoder as a WASI module using a pure C API.

The JavaScript path matters because it removes the need for a native plugin on the web, and the multithreading option is what stops in-browser encoding from being unusably slow. The WASI path matters for the opposite reason: server-side tooling and CLI environments where you want a small static binary rather than a full C++ runtime.

Python support is described as full for encoding and transcoding, working against either native or WASM modules, but explicitly still in the early stages of development. That caveat belongs in any evaluation: the Python layer is the natural one for a content build pipeline, and the project is telling you it is the least settled surface.

The tree backs up the multi-platform story with `CMakeLists.txt` for CMake builds, `basisu.sln` and three `.vcxproj` files for Visual Studio, `all_builds.py` for scripted builds across configurations, `appveyor.yml` for Windows CI, a `.pre-commit-config.yaml`, a `SECURITY.md`, `python/`, `webgl/`, `OpenCL/`, `example/`, `example_capi/` and `example_transcoding/`.

One file tells you how the tool is used in practice without opening anything else: `basisu_text_image.cpp` and `basisu_text_image.h`, which are a text-to-image path built into the command line tool. For a project about compressing images, having text rendering in the same binary saves you a step in every text-heavy asset pipeline.

Licensing is Apache 2.0 with three named caveats

The License and Legal section is unusually detailed, and a reader shipping a game or an application should read all of it.

The reference encoder library, the transcoder, and most specification documents in the repository are copyright 2016 to 2026 Binomial LLC, all rights reserved except as granted under the Apache 2.0 licence. Basis Universal is a trademark of Binomial LLC, and KTX is a trademark of The Khronos Group. There is a NOTICE file in the repository and a DEP5 file under `.reuse/` that gives the complete list of software and their licences.

The first caveat is the standard Apache 2.0 section 4(b) obligation: if you modify the Basis Universal reference source code, specifications, or wiki documents and redistribute the files, you must cause any modified files to carry prominent notices stating that you changed them. That is a real cost if you fork the reference encoder, which many studios do for a codec with this much performance tuning in it.

The second is that the encoder library is Apache 2.0 but pulls in third-party modules. The README names them and the directories: modules in `encoder/3rdparty` and in the `Zstd` directory load QOI images via qoiformat.org, DDS images via a tiny_dds project, and EXR images via tinyexr, handle Zstd compression, and unpack ASTC texture blocks. There is a LICENSES directory in the tree with the details.

The third is the trademark position. You can use the code under Apache 2.0; you cannot call your product Basis Universal or KTX without a licence from Binomial or Khronos. For a texture pipeline this is usually academic, but it matters if you are building a tool that other people download and misread as the official one.

How it compares to shipping BC7, ETC2 and PNG separately

The alternative is not another library, it is the status quo, and the comparison has to be honest in both directions.

Against shipping pre-encoded copies per platform, Basis Universal wins on storage and on pipeline simplicity. One encoded file replaces several, the README's stated goal is efficient distribution, and the runtime picks the format from a capability query the transcoder performs. For a title with a large texture set, the difference between one small file and five large ones is not a rounding error.

Against pre-compressed target formats at load time, Basis Universal loses on load time and on peak memory. A transcode is work: ETC1S is described as only slightly faster than libjpeg, and the other codecs are faster than that but still not free, and the runtime needs the decoded block data in memory. A project that currently ships BC7 files and lets the GPU upload them directly is doing less work at startup. Basis Universal is a bad fit for a first-frame-sensitive loading path unless you pre-transcode into a runtime cache.

Against the standard format tooling, the contrast is openness. KTX2 and Basis are documented formats with published specifications, and the codec work reuses standards like ASTC 4x4 and BC7 rather than inventing an opaque blob. That is the opposite of the older practice of shipping a proprietary container that only one vendor's tool can read.

Against the newest codecs specifically, be conservative. XUBC7 and XUASTC are recent, the deblocking optimisation landed in v2.50, and the v2.50 release entry carries no changelog to read, which is not a place to start if you need a documented change list. ETC1S and UASTC are the settled choices; the rest is where the interesting work is and where the rough edges are too.

Editorial conclusion

Basis Universal solves a specific and annoying problem: the same game or application has to look the same on a desktop GPU using BC7, a phone using ETC2, and a browser using ASTC, and shipping all three sets of assets is expensive. Its answer is to encode once into a supercompressed intermediate and transcode on the fly at load time, which the README frames as making GPU textures infrastructure rather than a per-platform chore. The cost is honest and stated plainly: transcoding is not free, the codec choices have real quality trade-offs against each other, and the newest codecs are the least settled. It is Apache 2.0, at v2.5 with v2.50 tagged 2026-08-03, and it builds to native, WebAssembly, WASI and Python. If your asset pipeline is the bottleneck, start with the transcoder and the KTX2 path, then read the codec specification wiki pages before choosing between ETC1S and XUASTC.

Frequently asked questions

What is Basis Universal used for?

It is a portable LDR and HDR GPU supercompressed texture transcoding system from Binomial LLC. You encode a texture once into an intermediate format, KTX2, .basis or DDS, and the transcoder converts it on the fly into whatever compressed GPU texture format the target hardware actually supports.

What is the difference between ETC1S and UASTC?

ETC1S is a supercompressed subset of ETC1 built for very fast transcoding, with low to medium quality and high compression. UASTC LDR 4x4 is a custom ASTC 4x4-like format for high quality at very fast transcoding speed, with or without rate-distortion optimisation. XUASTC pushes further into quality and bitrate scalability.

Which platforms can Basis Universal build for?

The C and C++ encoder and transcoder libraries compile to native code or to WebAssembly for web or WASI, and all features are reachable from JavaScript through a C++ wrapper with optional WASM multithreading. Python support for encoding and transcoding is available against native or WASM modules, though the README describes it as still in early stages of development.

What does RDO mean in this context?

RDO is rate-distortion optimisation, and it appears beside most of the codecs in the README. It means spending bits where they buy the most visible quality. XUASTC goes further: as of v2.50 the encoder supports in-loop deblocking through a global optimisation step using Stochastic Coordinate Descent, so it optimises for the output after the filter rather than before it.

What licence applies, and what are the catch?

The reference encoder, transcoder and most specifications are Apache 2.0, copyright 2016 to 2026 Binomial LLC, with LICENSES and a DEP5 file listing third-party modules. Modified and redistributed files must carry notices that they changed, the encoder loads QOI, DDS, EXR and Zstd third-party code, and Basis Universal and KTX remain trademarks.

Official sources

  1. BinomialLLC/basis_universal on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/binomialllc-basis-universal.svg)](https://hysenlabs.com/projects/binomialllc-basis-universal)