# PortableGL: OpenGL 3.x in one C header, with shaders as C functions

> A single-header implementation of the OpenGL 3.x core in clean C99, in the style of the stb libraries, that compiles as C++ and can draw into any 32 or 16 bit framebuffer. The two departures from the OpenGL API are deliberate: shaders are ordinary C functions rather than GLSL, and uniforms are a pointer to your own struct.

**rswinkle/PortableGL** — An implementation of OpenGL 3.x-ish in clean C

- Repository: https://github.com/rswinkle/PortableGL
- Stars: 1,286 · Forks: 61
- Language: C
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/rswinkle-portablegl

## What PortableGL is, and the quote it opens with

PortableGL is an implementation of OpenGL 3.x, mostly, in clean C99 as a single header library in the style of the stb libraries. That means it compiles cleanly as C++ and can be added to almost any codebase.

The README opens with a John Carmack quotation that explains the project better than a feature list would: because of the nature of Moore's law, anything that an extremely clever graphics programmer can do at one point can be replicated by a merely competent programmer some number of years later.

That is the thesis of a software rasteriser. The complexity of a hardware-accelerated graphics API is not a permanent property of the problem, it is the price of the hardware. Written in plain C with no dependencies, the same drawing model becomes something you can read, compile anywhere and step through in a debugger.

The scope statement is deliberately narrow. It can theoretically be used with anything that takes a 32 or 16 bit framebuffer or texture as input in any format, including just writing images to disk manually or using something like stb_image_write. The README says that should mean it supports almost everything, barring performance issues.

The project is MIT licensed, the primary language is C, and it is not archived with the last push on 2026-09-30. The releases are 0.100.0 from 2026-03-05, 0.99.0 from 2025-09-15 and 0.98.0 from 2024-05-06, so it is still on a zero version and still moving.

## The priority list, and where it puts API compatibility

The goals are listed roughly in order of priority, and the order is the design.

First, portability. Second, matching the API within reason, at the least matching features and abilities. Third, ease of use. Fourth, straightforward code. Fifth, speed.

And then the README is explicit that there are trade-offs between several of those, and gives the two examples that matter.

The first is shaders, where straightforward code and API matching beat API matching. Rather than write or include a GLSL parser and have a built-in compiler or interpreter, shaders are special C or C++ functions that match a specific prototype. So a shader is a function with a fixed signature that the rasteriser calls, not a string of GLSL that gets compiled at run time.

The second is uniforms, where ease of use and straightforward code beat API matching, because matching the API made no sense there and the library does it much more simply by passing a pointer to a user-defined struct.

Both of these are cases where the OpenGL API is a consequence of the hardware and of C's lack of reflection. A GLSL string is what you have to use when the driver has to compile at run time, and an OpenGL uniform setter is what you need when there is no way to introspect a struct's layout. A software rasteriser knows its own types at compile time, so it can take a pointer and a struct definition and skip all of that.

The practical effect is that code written against PortableGL does not port back to OpenGL, and code written against OpenGL does not port to PortableGL without rewriting the shader and the uniform handling. The API is OpenGL-shaped, not OpenGL-compatible.

## Shaders as C functions, and the colour buffer you choose

The shader model is the most surprising part of the design, and the most useful.

Instead of a GLSL string, you write a function with a specific prototype, and the rasteriser calls it per fragment or per vertex. That means your shader is compiled by your compiler, with your compiler's warnings, your debugger can step into it, and there is no compile time on the critical path and no compile failure at run time.

The cost is that you do not get a shader language's expressiveness. There is no GLSL library of built-ins beyond what the library provides, and no dynamic linking of shader source.

The other half of the model is the framebuffer. PortableGL supports arbitrary 32-bit and 16-bit colour buffer formats, selected at compile time, with several common ones ready to use out of the box, and the documentation points to `src/header_docs.txt` for the details. The format is a compile-time choice, so the library is specialised to your target's pixel layout, and you are expected to know what your framebuffer looks like.

That combination, a software rasteriser with a compile-time-chosen pixel format and C-function shaders, is why the library is described as usable with anything that takes a framebuffer as input. It is a rendering core, not an application framework, and the choice of buffer format is the contract with whatever is going to present the result.

## The directory structure, and how the single header is produced

The repository is laid out in a way that explains the workflow.

The `src/` directory contains the actual source files of `portablegl.h`, which are amalgamated with `generate_gl_h.py`, and `portablegl.h` at the repository root is the current dev version. So the single header is a build artefact checked into the tree, and the script that produces it is in the repository alongside the sources it concatenates. That is the standard arrangement for a stb-style project, and it means the sources are the truth and the header is generated from them.

The `examples/` directory has three subdirectories. `original/` holds the author's own examples in C and C++, and the C++ programs use rsw_math rather than glm, his own maths library. `classic/` holds ports of classic OpenGL programs and demos, currently just gears. And `webgl_lessons/` holds ports of the lessons from learningwebgl.com, based on his ports to OpenGL 3.3.

The `demos/` directory is unpolished, open ended programs demonstrating a wide variety of features, and the `backends/` directory has hello triangle programs using backends other than SDL2, with x11 and xlib, wayland, win32, gtk and qt named. Almost all the demos use SDL2.

The `external/` directory vendors Windows SDL2, GLM and ImGui, and `glcommon/` is a collection of helper libraries the author uses for graphics programming. `tools/` has premake5 binaries for Windows and mac, and `media/` holds models in his own simplified text format, along with screenshots and textures.

And `testing/` contains a formal regression and performance test suite, with the expected output frames for the regression tests and the output of the regression tests in a separate directory. That is a real test suite for a graphics library, and the expected frames are the interesting part, since they are how you assert that a change did not alter what is drawn.

## Building it, and the test suite you can run in a minute

There are no dependencies for PortableGL itself, other than a compliant C99 or C++ compiler. That is the whole dependency story, and it is the reason the library is portable rather than merely cross-platform.

To check that it compiles and runs, the instructions are two commands:

```
cd testing
make run_tests
...
./run_tests
All tests passed
```

The `make run_tests` target builds the test program, and running it produces the pass message. The testing README has more on the formal testing, and the examples README describes how to get SDL2 if you do not have it and how to use make to build the examples.

The build of the examples themselves uses premake5, given the `examples/premake5.lua` file and the `examples/Polished_Examples.sln` for Visual Studio. So there are two build systems in play, one for the library itself, which needs nothing, and one for the example programs, which needs a project generator and SDL2.

The examples are the documentation, and the README is frank about that. There is documentation including a minimal program in the comments at the top of `src/header_docs.txt`, but there is currently no formal documentation. The best way to learn is to look at the examples and the demos and compare them to equivalent OpenGL 3.3+ programs.

For the most part the author points at the Khronos material: the official OpenGL docs and reference pages are good for 90 to 95 percent of it as far as basic usage, with links to the 4.6 core reference, the 4.5 comprehensive reference and the tutorials and guides.

## How to learn it, and the two companion repositories

The learning path the author recommends is unusually well thought out for a project with no formal documentation.

His ports of the learnopengl.com tutorial code live in a separate repository, LearnPortableGL, and the README calls them the best resource, because they combine the original tutorials explaining the OpenGL aspects with his comments in the ported code explaining PortableGL's differences and limitations, at least the first time they appear. That is a genuinely good format, because a reader who already knows OpenGL can see exactly where the two diverge.

For the original examples and demos there is a comparison repository, opengl_reference, holding his equivalent programs, so the port can be read side by side with the thing it replaces.

The gallery shows what the result looks like: the classic gears demo, a sphere world, a backpack model, and a PortableGL port of Michael Fogleman's Craft, the Minecraft-like voxel engine, which is a good stress test for a software rasteriser because it is a large scene with lots of geometry.

And there is a shader workshop, on itch.io, for people who want to support the development or are interested in shader art, or both. The screenshots in that announcement show a Minkowski madness shader and a video greenscreen shader, which is a reminder that the library is used for things beyond the OpenGL-shaped programming it presents.

The `notes/` directory at the repository root and the `AGENTS.md` complete the picture of a project that is thinking about how people work with it.

## Where PortableGL is the wrong choice

The first wrong case is anything that needs hardware acceleration. This is a software rasteriser, and the README says the support for almost everything is bounded by performance issues. A modern GPU does in parallel what this does on the CPU, and no amount of tuning closes that gap for a real application.

The second is a project that needs the full OpenGL 3.x feature set, or any later one. The description says OpenGL 3.x, mostly, and the priority list puts API matching second behind portability, which means the library is deliberately allowed to diverge. Anything relying on the exact OpenGL semantics, on the extensions, or on the parts of the specification that only exist because of how GPUs are built, will not find them here.

The third is a team that wants to write GLSL. The shader model is the largest single divergence, and it means shader code cannot be shared with an OpenGL project, cannot be loaded at run time, and cannot be written by a shader artist who works in GLSL.

The fourth is a project that needs a maintained upstream with a stable API. The version numbers show the state of things, at 0.100.0 and still on a zero version, and the README says the author tries not to introduce bugs but that they occasionally slip in, as well as rarely breaking changes, and that at some point they will move to more frequent point releases for fixes and non-breaking changes and be more consistent with semantic versioning. A library that admits occasional breaking changes on a zero version is a library you vendor and pin.

The fifth is a beginner who wants to learn OpenGL. For that, the real OpenGL with a real driver is better, and it is what the learnopengl.com tutorials teach.

What PortableGL gives you is the OpenGL model without the GPU, in a form you can read in an afternoon, and that is a rare and valuable thing.

## Conclusion

PortableGL suits a developer who needs OpenGL-shaped drawing calls on a platform with no driver, an embedded target, a teaching exercise, or a codebase that wants a software rasteriser it can read end to end. It does not suit a project that needs the full OpenGL 3.x feature set or that needs hardware speed, since it is a software rasteriser. Before adopting it, read the priority list, because matching the OpenGL API is second on that list behind portability, and if you are porting from OpenGL code, expect the shaders and the uniform handling to be rewritten rather than translated.

## FAQ

### What is PortableGL?

It is an implementation of the OpenGL 3.x core, mostly, written in clean C99 as a single header library in the style of the stb libraries. It compiles cleanly as C++ and can be added to almost any codebase.

### How do shaders work in PortableGL?

They are not GLSL. Rather than write or include a GLSL parser and a built-in compiler or interpreter, shaders are special C or C++ functions matching a specific prototype, so they are compiled by your own compiler rather than at run time.

### How do I build and test PortableGL?

There are no dependencies beyond a compliant C99 or C++ compiler. Change into the `testing` directory, run `make run_tests` and then `./run_tests`, which prints that all tests passed. The testing directory holds a formal regression and performance suite with expected output frames.

### Does PortableGL need a GPU or a windowing system?

No. It can be used with anything that takes a 32 or 16 bit framebuffer or texture as input in any format, including writing images to disk manually. Almost all the demos use SDL2, but the `backends` directory shows hello triangle programs with x11 and xlib, wayland, win32, gtk and qt.

### Where is the documentation for PortableGL?

In the comments at the top of `src/header_docs.txt`, which includes a minimal program, but the README says there is currently no formal documentation and that the best way to learn is to read the examples and demos and compare them to equivalent OpenGL 3.3 programs.

## Sources

- [Issues](https://github.com/rswinkle/PortableGL/issues)
- [License: MIT](https://github.com/rswinkle/PortableGL/blob/master/LICENSE)
- [README](https://github.com/rswinkle/PortableGL/blob/master/README.md)
- [Releases](https://github.com/rswinkle/PortableGL/releases)
- [rswinkle/PortableGL on GitHub](https://github.com/rswinkle/PortableGL)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/rswinkle-portablegl
