Read Unity AssetBundles in JavaScript, in your browser or in Node.js. Browse the assets in a bundle, read text and script data, and turn textures and sprites into RGBA pixels.
Try the live demo — open a bundle, browse its contents, and preview its textures and sprites.
- The parser is written in TypeScript. Compressed texture decoding uses WebAssembly.
- You do not need Unity, .NET, or a native application installed.
- Packages for meshes, audio, Spine, shaders and animation build on the same core.
- A command line tool lists, inspects and extracts bundles:
npx unity-asset-reader-cli.
Install the core, the texture package and the Node.js file loader:
npm install unity-asset-reader unity-asset-reader-texture unity-asset-reader-nodeLoad a bundle (or a whole folder) and get every texture and sprite as pixels:
import { images } from "unity-asset-reader-texture";
import { loadPath } from "unity-asset-reader-node";
const env = loadPath("Build/StreamingAssets/bundles"); // a file, or a folder read recursively
env.files; // every unpacked file: [{ path: "CAB-…", data }, { path: "CAB-….resS", data }]
// Every Texture2D and Sprite, decoded. The WASM decoder loads on first use.
for await (const { rgba, width, height, path, name, formatName } of images(env)) {
console.log(path ?? name, width, height, formatName); // "assets/ui/icon.png" 256 256 "DXT5"
}- Bytes that are not on disk (an upload, a download): skip
unity-asset-reader-nodeand callload(bytes).bytesis aUint8Arrayor anArrayBuffer(aBufferis aUint8Arraytoo). Or callawait open(url)to fetch the file. - Saving a PNG:
encodePng({ data: rgba, width, height })fromunity-asset-reader-texturereturns the file's bytes, uncompressed. For smaller files, hand the RGBA to an image library, for examplesharp(rgba, { raw: { width, height, channels: 4 } }).png().toFile("out.png").
npm install unity-asset-reader unity-asset-reader-textureimport { open } from "unity-asset-reader";
import { initTexture, isImage, imageInfo, decodeImage } from "unity-asset-reader-texture";
// A browser has no package folder to read from, so tell it where the two WASM files are:
// a CDN, as here, or your own static folder (`npx texture2ddecoder-copy-wasm public/wasm`).
await initTexture({ wasmPath: "https://cdn.jsdelivr.net/npm/texture2ddecoder-wasm@1/wasm" });
const env = await open("a.bundle"); // fetches it; a File from an <input> works too
const icon = env.get("Assets/UI/Icon.png"); // an asset by its path in the Unity project
if (icon && isImage(icon)) { // a Texture2D or a Sprite
const { formatName, mipCount } = imageInfo(icon); // metadata, no decoding
const { rgba, width, height } = await decodeImage(icon); // RGBA, top row first
}Parsing is synchronous, so a big bundle blocks the page while it loads. Run it in a Web Worker:
examples/cdn.html does that, and the Bundler Guide has
the Vite and Next.js setups.
| What you need | Packages |
|---|---|
| Read bundles and their assets | unity-asset-reader |
| Decode textures and sprites too | unity-asset-reader + unity-asset-reader-texture |
| Load files or folders from disk | Add unity-asset-reader-node |
| Export meshes as OBJ or glTF | Add unity-asset-reader-mesh |
| Convert audio clips to WAV (or Vorbis clips to Ogg) | Add unity-asset-reader-audio |
| Export Spine skeletons (data, atlas, page textures) | Add unity-asset-reader-spine |
| Write shaders as AssetStudio's inspection text (GLSL, Metal, SPIR-V) | Add unity-asset-reader-shader |
Decode animation clips into curves, or export an animated scene to glTF (.glb) |
Add unity-asset-reader-animation |
| Decode raw texture blocks without reading Unity files | texture2ddecoder-wasm |
| List, inspect or extract bundles from the command line, without writing code | npx unity-asset-reader-cli |
How they fit together:
- Every package except
texture2ddecoder-wasmbuilds on the core. The feature packages listunity-asset-readeras a peer dependency, so install it next to them. - The texture package installs
texture2ddecoder-wasmautomatically. - The command line tool is an application, not a library: it installs the core and the packages it uses itself.
- Install only what you need: reading text or script data does not load the texture decoder.
- Each package has its own version.
What each package does, and what it leaves out:
-
Core (
unity-asset-reader): unpacks bundles, lists assets and reads their data. Useload(bytes)for bytes you already have, orawait open(url)to fetch a file. ItsGltfWriterwrites binary glTF 2.0 files from data you give it (meshes, nodes, skins, morph targets, animations). It does not decode textures to pixels or audio to WAV, does not convert Unity meshes, and does not read the disk. It throwsUnsupportedErroron a compression, container or Unity version it does not read. -
Textures (
unity-asset-reader-texture): convertsTexture2DandSpriteassets to RGBA pixels withawait decodeImage(asset). It does not decode mip levels past the first,Cubemap,Texture2DArrayorTexture3Dassets, or a few formats such asDXT3andARGBFloat(see Texture formats). -
Node.js (
unity-asset-reader-node): loads a file or folder withloadPath(), including split bundles and resource sidecars. It does not run in browsers, does not stream (each file is read whole), and does not look up dependency bundles itself: pass them, or their folder. -
Meshes (
unity-asset-reader-mesh): writes aMeshas Wavefront OBJ text withmeshToObj(), as AssetStudio exports it, or as a binary glTF 2.0 file withmeshToGltf()(built on core'sGltfWriter). It does not export materials, bones, blend shapes or UV channels past the first. -
Audio (
unity-asset-reader-audio): converts anAudioClipto a WAV file withfsb5ToWav(clip.data.audioData), for PCM and ADPCM sound. Converts Vorbis sound to Ogg withfsb5ToOgg(), given its setup header (none ships with the package). It does not convert MPEG, ATRAC9, XMA, Opus or other console codecs, or clips from before Unity 5.0, which hold their file as it is. -
Spine (
unity-asset-reader-spine): finds each spine-unity skeleton's data, atlas text and page textures withspineSkeletons(env). It does not parse the skeleton data or atlas text (both come back as stored), and does not read bundles built without type trees. -
Shaders (
unity-asset-reader-shader): writes aShaderas the inspection text AssetStudio exports, withshaderToText(): OpenGL and Metal source, and SPIR-V disassembly. It does not write Direct3D programs, and its text is not a shader Unity can compile. -
Animation (
unity-asset-reader-animation):clipCurves(env, clip)decodes anAnimationClipinto the curves AssetStudio's.animexport holds: each curve's path, attribute and keyframes, and the clip's events.sceneToGltf(env, root, { clips })exports a scene (nodes, skinned meshes, blend shapes) with its clips as a glTF 2.0.glb. A humanoid clip's muscles (fingers aside) andRootQare converted to bone rotations.
It does not write
.animYAML. Of a humanoid clip, it does not convert the body position (the Hips keep their own position), the finger muscles, the root motion or the IK goals. -
Command line (
unity-asset-reader-cli):ls,info,extractanddumpfor one bundle, file or folder, built on the packages above.extractwrites images as PNG, audio as WAV or Ogg, meshes as OBJ or glTF, shaders and text as text. It does not take several paths or globs (pass their folder), and does not export what the packages above do not (Direct3D shaders, MPEG audio, Vorbis audio without its setup headers). -
Standalone decoder (
texture2ddecoder-wasm): decodes compressed texture blocks to BGRA. It does not read Unity files.
- Quick Start: extract a bundle in Node.js, draw a texture in a browser.
- Bundler Guide: Vite, Next.js, CDN without a bundler, Node.js ESM and CommonJS, Workers.
- Package READMEs: the API reference, and what is supported and what is not.
unity-asset-reader: containers, compression, classes, tested Unity versions, errors,bigintand JSON.unity-asset-reader-texture: texture formats, sprites, platforms.unity-asset-reader-node:loadPath().unity-asset-reader-mesh:meshToObj(),meshToGltf().unity-asset-reader-audio: FSB5 codecs to WAV.unity-asset-reader-spine:spineSkeletons().unity-asset-reader-shader:shaderToText().unity-asset-reader-animation:clipCurves(),sceneToGltf().unity-asset-reader-cli: thels,info,extractanddumpcommands.
examples/: a CDN page that reads a bundle in a Worker and draws its textures.
This is the short answer. The full tables, with what each claim is tested on, are in the
package READMEs:
containers, compression, classes and Unity versions,
texture formats, platforms and sprites, and
texture2ddecoder-wasm's block formats.
A few terms used below:
- A SerializedFile is the file inside a bundle that holds the objects (assets). A game
build also ships loose ones, outside any bundle (
.assets,level0, ...). - A type tree is the field layout Unity can store next to the objects. With it, any class can be read; without it, only classes that have a hand-written reader.
- A version-stripped file is built with
AssetBundleStripUnityVersion, so it does not say which Unity version wrote it.
- Containers:
- UnityFS.
- UnityWeb and UnityRaw (legacy web player bundles).
UnityWebData1.0(a WebGL.datafile).- A gzip-wrapped file.
- Loose SerializedFiles (
.assets,level0,globalgamemanagers). .resSand.resourceresource files.- Split files (
.split0,.split1, ...), throughunity-asset-reader-node.
- Bundle compression: none, LZ4, LZ4HC, LZMA.
- SerializedFile formats: 2 to 23, little- and big-endian.
- Classes: any class, through its type tree. These classes have a hand-written reader,
which also reads bundles built without type trees:
AssetBundle,TextAsset,MonoBehaviour,MonoScript,Material,Shader,Texture2D,Sprite,SpriteAtlas,AudioClip,VideoClip,Font,MovieTexture,Mesh(its vertices and triangles decoded),Avatar,AnimatorController,Animator,AnimatorOverrideController,AnimationClip,Animation,GameObject,Transform,RectTransform,MeshFilter,MeshRendererandSkinnedMeshRenderer. - Texture formats: output is RGBA8, top row first, first mip level.
- 22 plain formats (
RGBA32,RGB565,RHalf,RGB9e5Float,YUY2,RGBA64, ...), in TypeScript. - Through WASM: BC1 (
DXT1), BC3 (DXT5), BC4 to BC7; ETC1, ETC2 and their 3DS variants; EAC R and RG, signed too; PVRTC 2 and 4 bpp; ATC; ASTC LDR and HDR at 4x4 to 12x12. - Crunch (
DXT1Crunched,DXT5Crunched,ETC_RGB4Crunched,ETC2_RGBA8Crunched), through WASM.
- 22 plain formats (
- Sprites:
- Cut out of their texture, or of their
SpriteAtlaswhen it is loaded. - Packer flips and rotations undone.
- Optional transparency outside a tight mesh.
- The sprites a Unity 6000.6
SpriteAtlasholds itself, which have noSpriteobjects, throughpackedSprites()andimages().
- Cut out of their texture, or of their
- Audio (through
unity-asset-reader-audio):AudioClipto WAV for FSB5 banks of PCM (8, 16, 32 bit), IMA ADPCM, FADPCM and GCADPCM sound. FSB5 is the FMOD sound bank format Unity stores clips in.- To Ogg for Vorbis sound, given a caller-supplied setup header table.
- Tested to match AssetStudio's WAV on the PCM clips and the mono and stereo IMA ADPCM clips of the 2019.4, 2020.3 and 6000.3 bundles.
- Shaders (through
unity-asset-reader-shader):Shaderto AssetStudio's inspection text.- OpenGL, OpenGL ES and Metal programs as their source; Vulkan programs as SPIR-V disassembly.
- Tested to match AssetStudio's
.shaderfile on the OpenGL ES, Vulkan and Metal shaders of the 2019.4 and 2020.3 bundles. - Tested to match its converter's text (on UnityPy's type tree) on those of the 6000.3 and
6000.6 bundles without an
Intproperty, several keyword variants per stage included.
- Platforms: Xbox 360 textures are byte-swapped back, and Switch textures deswizzled. Textures of every other build target are decoded as stored, except PS4 and PS5 ones, which are refused (#130).
- Runtimes: browsers and Web Workers (ES2020, WebAssembly for textures); Node.js
^20.19.0 || >=22.12.0, withimportandrequire.
Tested on bundles built by the Unity editor:
- Unity 2019.4.41f2, 2020.3.30f1 and 6000.3.25f1. That covers SerializedFile
formats 21 and 22 and UnityFS formats 7 and 8; LZ4, LZMA and uncompressed blocks; with and
without type trees; and version-stripped files.
- PVRTC comes from 2019.4 only (Unity 6 no longer writes it).
- Sprites and atlases come from 2019.4 and 6000.3.
- Unity 6000.6.4f1: SerializedFile format 23 metadata, type-tree dumps,
SpriteandSpriteAtlas. Shadercomes from 2019.4, 2020.3, 6000.3.26f1 and 6000.6 (DX11, GLES3 and Vulkan; Metal from 2020.3 and 6000.3).Avatar,AnimatorController,Animator,AnimatorOverrideController,AnimationClip,Animation,GameObject,Transform,RectTransform,MeshFilter,MeshRendererandSkinnedMeshRenderercome from 2019.4, 2020.3, 6000.3.26f1 and 6000.6.
Tested on generated or hand-written bytes only. No real Unity build of these versions or containers is in the tests:
- the class readers' version gates from Unity 3.4 to 6000.6;
- SerializedFile formats 6, 8 and 15;
- UnityFS format 6;
- UnityWeb and UnityRaw formats 2, 3, 4 and 6;
UnityWebDataand gzip.
Handled in code:
- SerializedFile formats 2 to 23 (Unity 2020.1 to 6000.3 write 22, 6000.6 writes 23). Type tree reads work on every one of them, whatever the Unity version.
- The class readers know the layouts from Unity 3.4 up to 6000.6, and read a newer version with
the newest layout they know. Some start later:
Spritefrom 4.3,VideoClipfrom 5.6,SpriteAtlasfrom 2017.1, andMesh,Shader, the animation classes and the scene classes from 2019.1. - Unity 2019.1 is the supported floor. Readers added since that decision (
Mesh,Shader,Avatar,AnimatorController,Animator,AnimatorOverrideController,AnimationClip,Animation,GameObject,Transform,RectTransform,MeshFilter,MeshRenderer,SkinnedMeshRenderer) start there. The older readers keep their older layouts. SpriteAtlas's 6000.6 layout is tested on Unity 6000.6.4f1 bundles, and so are the texture package's cuts of the sprites such an atlas holds.- A class reader refuses a version older than its first layout.
readTypeTree()still reads such an object. Texture2Dis the exception: it has no floor. It has gates at 2.6 and 3.0 that no test covers, and reads any older version with its oldest layout.
Version-stripped files (AssetBundleStripUnityVersion):
-
Type tree reads work.
-
A class reader reads the object when its bytes or the SerializedFile format decide the layout, and refuses it otherwise:
Texture2D,MovieTextureandMonoScript: always refused.Material,Sprite,SpriteAtlas,Meshand the animation classes: refused outside formats 18 to 21.Shader: refused outside format 21.
So these classes are refused in every version-stripped file from Unity 2020.1 on.
-
If you know the editor that built the file, pass it to
load()oropen()as theunityVersionfallback. The class readers then read such objects too. -
The table per class is in the core README.
Each of these throws UnsupportedError, naming what it found:
- Compression: LZHAM, brotli (a WebGL build's
.brfiles), zstd. - Containers:
UnityArchive, zip archives, encrypted bundles (UnityCN and other game-specific encryption). - Texture formats:
DXT3,ARGBFloat,RGBFloat,BGR24, and the signed plain formats.- Textures built for PS4 or PS5.
- Sprites whose alpha texture (ETC1 split alpha) is not the size of their texture.
- A
Spriteobject that points at a Unity 6000.6SpriteAtlas(the atlas' own packed sprites are supported).
- Meshes: a submesh of lines or points, a vertex format Unity does not define.
- Audio:
- Vorbis without a setup header table from the caller (none ships).
- The other FSB5 codecs that are not PCM, ADPCM or Vorbis (MPEG, ATRAC9, XMA, ...).
- GCADPCM of more than one channel.
- Sound that is not an FSB5 bank (
AudioClips from before Unity 5.0).
- Shader text: Direct3D programs (they need Windows' D3DCompiler), and
Intproperties, on which AssetStudio fails too.
Not provided at all:
- decoding video;
- exporting models as FBX;
- mip levels other than the first;
- the
Cubemap,Texture2DArrayandTexture3Dclasses; - JPEG encoding;
- writing or repacking bundles.
git clone --recurse-submodules https://github.com/fatal10110/unity-asset-reader.git
cd unity-asset-reader
npm ci
npm run verify # build, test, check:browser, no-C# guard- Building the WASM decoder (
npm run build:wasm) needs Docker. - Setup and conventions: CONTRIBUTING.md.
- The design and its decisions: docs/unity-asset-reader-plan.md.
- The rules for changes: docs/unity-asset-reader-rules.md.
See the feature and format comparison for a ✓ / ✗ table of Unity readers and texture decoders.
This project stands on the work below. The reader packages are a derivative port, not a clean-room rewrite:
- Every ported file starts with a line naming the file it came from.
- The copyright notices are kept in
NOTICEand in each package'sNOTICEorLICENSE.
| Project | License | What it is used for |
|---|---|---|
| AssetStudio, Razviar's fork of Perfare's AssetStudio (© Perfare, RazTools, Razviar) | MIT | The source of truth for behaviour. The bundle, SerializedFile and type tree readers, the class readers, the plain texture conversion, the Xbox 360 byte swap, sprite cropping and the tight-mesh mask, loadPath()'s file handling, the WAV header and the shader text are hand-ported from it. Also the oracle for the export goldens: scripts/make-export-goldens.py runs its exporters, outside the repo. |
| UnityPy (© K0lb3) | MIT | The oracle that generates the parsing goldens (scripts/make-goldens.py). Also ported where AssetStudio lacks something or disagrees: the UnityFS header version gates, the [SerializeReference] registry, the common-string table and the Switch texture deswizzle. |
| AssetRipper TypeTreeDumps and Tpk (ds5678) | TypeTreeDumps: no license stated; Tpk: MIT | Indirectly: the type tree data in UnityPy's TPK table, which the common-string table and several class readers' version gates were taken from. |
| AssetsTools.NET (© nesrak1) | MIT | UnityPy's Switch deswizzle, ported into unity-asset-reader-texture, is based on its SwitchSwizzle.cs. |
| ImageSharp.Drawing (© Six Labors) | Apache-2.0 | The sprite tight-mesh triangle fill in unity-asset-reader-texture is a modified TypeScript translation of parts of v1.0.0-beta15, the version AssetStudio uses, so the masks match to the pixel. |
| ImageSharp (© Six Labors) | Apache-2.0 | The sprite variant-atlas resize in unity-asset-reader-texture is a modified TypeScript translation of parts of v2.1.3, the version AssetStudio uses, so the resized sprites match to the byte. |
| Fmod5Sharp (© 2021 Sam Byass) | MIT | The FSB5 parser, the ADPCM decoders and the Vorbis to Ogg rebuild of unity-asset-reader-audio are hand-ported from it. |
| python-fsb5 (© 2016 Simon Pinfold) | MIT | Second reference for the FSB5 parser of unity-asset-reader-audio. |
| CSspv (© 2017 Matthäus G. Chajdas) | BSD-2-Clause | The SPIR-V disassembler of unity-asset-reader-shader is hand-ported from it, as AssetStudio vendors it. |
| smol-v (© 2016-2018 Aras Pranckevicius) | MIT or public domain | The SMOL-V format Unity stores Vulkan programs in; the decoder of unity-asset-reader-shader is ported from AssetStudio's C# port of its decoder, and the decoder of its newer encoding (Unity 2020.3 and later) from smol-v's source/smolv.cpp. |
| texture2ddecoder (© K0lb3) | MIT | The C++ block and Crunch decoders that texture2ddecoder-wasm compiles to WASM (a git submodule). |
| Codecs inside texture2ddecoder: Perfare's AssetStudio (ATC, BCn), mikunyan (ASTC, ETC, PVRTC), FP16, BinomialLLC/crunch, Unity-Technologies/crunch | MIT; MIT; MIT; public domain; zlib | The block decoders, half floats, and Crunch and Unity Crunch unpacking. |
| fflate | MIT | Runtime dependency of unity-asset-reader: gzip and zlib, synchronously. |
| lzma1 | MIT | Runtime dependency of unity-asset-reader: LZMA, synchronously and without WASM. |
| Emscripten | MIT or University of Illinois/NCSA | Compiles texture2ddecoder to the WASM module and generates its JavaScript loader (the pinned emscripten/emsdk:4.0.7 Docker image). |
Development only, not shipped:
- TypeScript (Apache-2.0), Rollup, esbuild and tsx (MIT) build and test the packages.
- Playwright (Apache-2.0) runs the browser smoke test.
- The test bundles are built with the Unity Editor from our own projects
(
fixtures/BUILDING.md).
MIT, with two packages that also include code under another license:
unity-asset-reader-texture(packages/texture) isMIT AND Apache-2.0. Its tight-mesh fill and variant-atlas resize are derived from ImageSharp.Drawing and ImageSharp, and are under Apache-2.0 (see itsNOTICEandLICENSE-APACHE).unity-asset-reader-shader(packages/shader) isMIT AND BSD-2-Clause. Its SPIR-V disassembler is ported from CSspv, and is under BSD-2-Clause (see itsNOTICEandLICENSE-BSD).
The upstreams the repo derives from (AssetStudio, UnityPy and others) are listed in
NOTICE. Each published package ships its own NOTICE, which is the authoritative one
for its tarball.