Library / SDK
syoyo/tinygltf avatar
syoyo/tinygltf

tinygltf v3: a header-only C11 glTF 2.0 loader and saver

Header only C11 tiny glTF 2.0 library

2,537 stars503 forksHTMLMIT

At a glance

What is it?
TinyGLTF v3 replaces the old C++ implementation with a pure C11 runtime, an arena allocator and structured errors. It is a good fit for engines and tools that want glTF parsing without STL or exceptions, and a poor fit for anyone who needs the deprecated C++ API.
Who is it for?
Adopt TinyGLTF v3 if you are writing C11 or C++17 code that must load or save glTF 2.0 without STL, RTTI or exceptions, and if binding a POD API to another language matters to you. Do not adopt it if you depend on the old C++ API in tiny_gltf.h, which the README says has moved to attic/ and is no longer built, tested or installed, or if you want the library to fetch and decode images for you by default.
Can I use it commercially?
Yes. MIT 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 62 days ago.
What is it written in?
Mainly HTML, according to GitHub's language statistics.

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

Editorial analysis

What TinyGLTF v3 solves, and who it is for

glTF 2.0 is a Khronos interchange format for 3D scenes, and parsing it means walking a JSON document, resolving buffers and accessors, and validating indices before anything downstream touches them. TinyGLTF v3 is a C11-first implementation of that loader and writer. The README describes a pure C POD API spread across three files: tiny_gltf_v3.h, tiny_gltf_v3.c and tinygltf_json_c.h. There is no STL, no RTTI and no exception requirement, which is the point. The README states the API is easy to bind to other languages, and a flat C surface is exactly what makes that true.

The audience is narrow but real. If you are embedding an asset pipeline inside a C engine, a console runtime, a WebAssembly build or a language binding, a C++ library that throws and allocates per node is friction. TinyGLTF v3 targets that reader. It is not aimed at someone who wants a scene graph, a renderer or a material system. It loads and saves the file format and stops there.

Arena allocation and one-call teardown

The design choice with the widest consequences is memory. The README states that all parse-time allocations come from a single arena, and that a single tg3_model_free() frees everything. For a loader that builds a tree of buffers, buffer views, accessors, meshes and nodes, this removes the usual failure mode where one error path leaks a subtree. You call free once, on the model, and the arena goes with it.

The trade-off is that you cannot free individual nodes while keeping the rest of the model alive. That is fine for a load-then-use pattern and awkward if you want to stream a huge scene and evict parts of it. The README also describes opt-in streaming parse and write through user-supplied callbacks, which is the escape hatch for the second case, but the arena model is the default one and it shapes how you write the surrounding code.

Error handling follows the same philosophy. tg3_error_stack holds machine-readable entries with severity levels and source locations, so a failed parse gives you a list rather than one string. The example in the README loops over errors.count and prints severity and message per entry, which is what you want when a file fails deep inside an accessor.

Installing TinyGLTF v3 and parsing a first file

There is no package manager step in the README. Installation is copying three files into your project: tiny_gltf_v3.h, tiny_gltf_v3.c and tinygltf_json_c.h. Compile tiny_gltf_v3.c as C11 or newer. The README notes that the legacy TINYGLTF3_IMPLEMENTATION include path still works for compatibility, but the v3 layout is a separate translation unit.

If you want tg3_parse_file() to use the stdio-backed filesystem helpers, define TINYGLTF3_ENABLE_FS when building tiny_gltf_v3.c. The README states that filesystem and image I/O are opt-in and off by default, so without that define you are responsible for supplying bytes yourself.

The include is a single line:

c
#include "tiny_gltf_v3.h"

The README gives this loading sequence. It initializes options and the error stack, parses with a max depth of 10, prints each error if the call does not return TG3_OK, then frees both the model and the error stack:

c
tg3_parse_options opts;
tg3_error_stack errors;
tg3_model model;

tg3_parse_options_init(&opts);
tg3_error_stack_init(&errors);

tg3_error_code err = tg3_parse_file(&model, &errors, "scene.gltf", 10, &opts);
if (err != TG3_OK) {
    for (uint32_t i = 0; i < errors.count; i++) {
        fprintf(stderr, "[%d] %s\n", (int)errors.entries[i].severity,
                errors.entries[i].message ? errors.entries[i].message : "(null)");
    }
}
// ... use model ...
tg3_model_free(&model);
tg3_error_stack_free(&errors);

On success you get a populated tg3_model and an empty error stack. On failure the loop prints one line per entry. To check your own build before touching your assets, the README documents a self-test path: run make in tests/ and execute ./tester_v3_c for the internal suite, or pass a file to parse a single asset. The top-level Makefile wraps this as make test.

Hardening for untrusted input, and what it does not cover

The README is explicit that the library is hardened against untrusted input: URI sanitization, post-parse index-bounds validation that is on by default, and strict numeric range checks. The validation can be turned off with tg3_parse_options.validate_indices = 0, and the README points to a Security Considerations block at the top of tiny_gltf_v3.h for the details. A libFuzzer harness lives in tests/v3/fuzzer/, and crafted regression inputs sit in tests/v3/security/ and seed the fuzzer corpus.

That is a stronger story than most small parsers tell, but it is not a sandbox. The README does not claim the library isolates a hostile file from the rest of your process, and disabling validate_indices puts the bounds checking back on you. If you parse files from the network, the default-on validation is the setting you want, and the documented opt-out exists for callers who have already validated elsewhere. Treat that flag as a deliberate decision, not a performance knob to flip on a hunch.

One more boundary: image decoding is optional. TINYGLTF3_ENABLE_STB_IMAGE is off by default, and when enabled it pulls in stb_image.h. If your pipeline expects the loader to hand you decoded pixels without any configuration, it will not.

The C++ API is in attic/, and that is the migration cost

The most consequential fact for existing users is that the previous C++ implementation has been deprecated. The README states that tiny_gltf.h, tiny_gltf.cc, tinygltf_json.h, json.hpp, loader_example.cc, the C++ examples and tests have moved to attic/ for reference, and that they are no longer built, tested or installed. TinyGLTF v3 C is described as the mainline.

If your project includes tiny_gltf.h today, an upgrade is a port, not a version bump. The repository does keep parity work in view: tests/tester_v3_c_v1port.c is described as a v3 C port of the legacy unit tests, kept for behavioral parity with the old C++ API. That tells you the maintainers care about matching old behavior, but it does not give you a compatibility shim. Plan the port as real work, and read the v1 port tests to see which behaviors were considered worth preserving.

A second cost is the JSON backend. TinyGLTF v3 uses its own parser and serializer in tinygltf_json_c.h, described as locale-independent and pure C, built on fast_float for number parsing and dragonbox for serialization. That is a deliberate split from the old json.hpp dependency, and it means number formatting and parsing behavior may differ from what your old code observed.

How it compares with fastgltf, and when neither fits

The related searches include fastgltf vs tinygltf, and the difference is architectural rather than cosmetic. fastgltf is a C++17 library, so it assumes a C++ toolchain and can use C++ facilities directly. TinyGLTF v3 goes the other way: the README describes a C11 POD API with no STL, no RTTI and no exceptions required, plus an optional C++20 coroutine facade that is auto-detected, with C17 and C++17 as the default. If your host language is C, Rust, Zig, or a scripting runtime, the C surface is the reason to pick TinyGLTF v3. If your project is already C++17 and you want the ergonomics that come with it, the C API is a layer you have to write yourself.

There are also cases where a glTF loader is simply the wrong tool. TinyGLTF v3 is a format library, not a renderer and not an asset pipeline. It will not resolve a scene graph into draw calls, it will not decode images unless you enable stb_image, and it will not fetch remote buffers for you. If what you actually need is a runtime that draws the file, you are looking one layer up.

Maintenance, licence and what an upgrade costs

The repository is not archived, and the last push was on 2026-08-02, which is the same date as the v3.0.1 release. Before that, v3.0.0 landed on 2026-03-23 and v2.9.7 on 2025-11-02. The README's Status section says TinyGLTF v3 is the mainline release and that API additions and behavior changes go through the usual review process. It does not promise a deprecation window or a stability guarantee for the C API, so pin a commit or tag if you need reproducibility.

TinyGLTF itself is MIT licensed. The README lists third-party code inside tinygltf_json_c.h: fast_float under Apache License 2.0, MIT or Boost 1.0, and dragonbox under Apache 2.0 with LLVM exceptions or Boost 1.0. stb_image.h is optional and public domain. Those are permissive terms, but a header-only library means the third-party code ships inside your binary, and some of those licences carry notice requirements. Read the LICENSE file and the upstream notices rather than assuming MIT covers everything in the tree.

Upgrade cost is dominated by the C++ to C port described above. For a new project there is no migration at all, which is the cheapest position to be in.

Editorial conclusion

Adopt TinyGLTF v3 if you are writing C11 or C++17 code that must load or save glTF 2.0 without STL, RTTI or exceptions, and if binding a POD API to another language matters to you. Do not adopt it if you depend on the old C++ API in tiny_gltf.h, which the README says has moved to attic/ and is no longer built, tested or installed, or if you want the library to fetch and decode images for you by default. Before committing, verify three things: the header and source compile as C11 in your toolchain, tg3_parse_file returns TG3_OK on one of your own assets with TINYGLTF3_ENABLE_FS defined, and the post-parse index validation in tg3_parse_options is left on unless you have measured a reason to disable it.

Frequently asked questions

How do I use TinyGLTF v3 to load a glTF file?

Copy tiny_gltf_v3.h, tiny_gltf_v3.c and tinygltf_json_c.h into your project, compile the .c file as C11 or newer, and define TINYGLTF3_ENABLE_FS if you want tg3_parse_file() to use the stdio-backed filesystem helpers. Then initialize tg3_parse_options and tg3_error_stack, call tg3_parse_file, and free the model with tg3_model_free when you are done.

Do I need the old tiny_gltf.h C++ header with TinyGLTF v3?

No. The README states that the previous C++ implementation, including tiny_gltf.h, tiny_gltf.cc and tinygltf_json.h, is deprecated and has moved to attic/ for reference, and that it is no longer built, tested or installed. TinyGLTF v3 C is the mainline.

Is TinyGLTF v3 header only?

The project describes itself as header only, but the v3 layout is tiny_gltf_v3.h plus tiny_gltf_v3.c plus tinygltf_json_c.h, and the README says to compile tiny_gltf_v3.c as C11 or newer. The legacy TINYGLTF3_IMPLEMENTATION include path remains available for compatibility.

Does TinyGLTF v3 load images and files by default?

No. The README states that the filesystem and image I/O options, TINYGLTF3_ENABLE_FS and TINYGLTF3_ENABLE_STB_IMAGE, are off by default, so you control when and how assets are loaded. Enable them at build time if you want the library to handle that for you.

Can TinyGLTF v3 validate indices in an untrusted glTF file?

Yes. Post-parse index-bounds validation is on by default, and the README says it can be turned off with tg3_parse_options.validate_indices = 0. The library also performs URI sanitization and strict numeric range checks, and a libFuzzer harness under tests/v3/fuzzer/ exercises the parser.

How do I build and run the TinyGLTF v3 tests?

The README documents several paths. Run make -C tests && make -C tests run for the plain make flow, use cmake -B build && cmake --build build && ctest --test-dir build --output-on-failure, or use meson setup build && meson compile -C build && meson test -C build. The top-level Makefile exposes the same thing as make test.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. syoyo/tinygltf on GitHub
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/syoyo-tinygltf.svg)](https://hysenlabs.com/projects/syoyo-tinygltf)