Library / SDK
emoon/minifb avatar
emoon/minifb

MiniFB: a small C frame buffer for cross-platform windows

MiniFB is a small cross platform library to create a frame buffer that you can draw pixels in

1,386 stars105 forksCMIT

At a glance

What is it?
MiniFB gives C and C++ programs a minimal path from an application-owned pixel buffer to a native or browser window, plus keyboard and mouse input. It suits small graphics tools, prototypes, games, and diagnostics that do not need a rendering framework, but its API coverage and present semantics vary by backend.
Who is it for?
Adopt MiniFB when the application already owns its rendering loop, accepts a hand-managed 32-bit buffer, and only needs the documented window and input operations. Do not choose it for a full engine, unsupported decoration flags, or environments without a suitable 32-bit display.
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 7 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 October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Why MiniFB exists in a C application

MiniFB solves a narrow graphics integration problem: moving pixels that an application has already rendered into a window, then reporting keyboard, mouse, and lifecycle events back to that application. It is aimed at C and C++ developers who want this boundary without adopting a larger rendering stack. The library does not provide shapes, textures, shaders, a scene graph, text rendering, or an asset pipeline. The application draws every pixel itself and remains responsible for the allocation, lifetime, and contents of the frame buffer.

That division makes MiniFB relevant to command-line experiments turned graphical, software-rendered demos, pixel-oriented diagnostics, small games, and applications that already have their own draw loop. It also creates a clear adoption test. If the application expects an engine to allocate surfaces, choose a graphics API, draw primitives, or manage a composition graph, MiniFB is too small for that role. Its value lies in accepting a buffer that already exists and copying it to a platform window.

The repository presents MiniFB as cross-platform, but support is not uniform. Windows has GDI and OpenGL backends, macOS has Cocoa and Metal, and Linux or Unix has X11 and Wayland. iOS, Android, WebAssembly, and MS-DOS are available with reduced or partial API coverage. The README says the library depends only on the relevant system libraries for each platform. The MIT license also keeps the licensing footprint simple, although it does not alter the application developer's responsibility for platform, native-library, and distribution requirements.

The pixel-buffer path through mfb_update

The core data flow is direct. A caller opens a window with mfb_open() or mfb_open_ex(), allocates an array of 32-bit pixels, renders into that array, and passes it to mfb_update() or mfb_update_ex(). MiniFB copies the supplied pixels to the window. It neither allocates nor frees the buffer, so the caller must size it as width multiplied by height pixels, with four bytes per pixel.

The distinction between mfb_update() and mfb_update_ex() is explicit dimensions. mfb_update() relies on the dimensions associated with the window, while mfb_update_ex() receives width and height for the supplied buffer. The extended form checks a null buffer, zero dimensions, and overflow in the internal stride calculation. mfb_open() and mfb_open_ex() similarly reject zero dimensions and a width whose four-byte stride would overflow.

Return values define the loop's failure handling. MFB_STATE_OK means the frame was accepted. MFB_STATE_EXIT means the window was closed and destroyed internally, so the caller must stop and must not use the handle again. Other values indicate a frame-level error such as an invalid window, invalid buffer, or backend failure while leaving the window alive. This contract is useful because a transient update error does not imply that every handle must be discarded.

The library also provides timers, FPS control, viewport functions, per-window custom data, cursor control, and state queries in addition to callbacks. Viewport coordinates use drawable coordinates. mfb_set_viewport() rejects zero width or height and bounds that exceed the current drawable size. These are useful controls, but the responsibility for rendering stays with the caller.

Building MiniFB and opening the first window

The README points readers to its platform-specific Windows, X11, Wayland, macOS, iOS, Android, WebAssembly, and DOS build notes. The repository includes CMakeLists.txt, a cmake directory, headers, sources, tests, and platform examples. However, the README does not state the platform build command or dependency configuration commands. This article does not invent a cmake invocation that may not match the project's options.

The documented first-use program is concrete. The following example opens an 800 by 600 resizable window, allocates a matching 32-bit pixel buffer, submits it, and exits when synchronization ends or an update fails:

c
int main() {
    struct mfb_window *window = mfb_open_ex("my display", 800, 600, MFB_WF_RESIZABLE);
    if (window == NULL)
        return 0;

    uint32_t *buffer = malloc(800 * 600 * 4);

    mfb_update_state state;
    do {
        // TODO: add some fancy rendering to the buffer of size 800 * 600

        state = mfb_update_ex(window, buffer, 800, 600);

        if (state != MFB_STATE_OK)
            break;

    } while(mfb_wait_sync(window));

    free(buffer);
    buffer = NULL;
    window = NULL;

    return 0;
}

The caller should see a window remain open while mfb_wait_sync(window) continues to return true. Once the window is closed, a later update returns MFB_STATE_EXIT, or synchronization ends, the loop breaks. A production program still needs to render meaningful values into buffer and check the result of malloc(), because the short example does not include either step. The repository also points to examples/noise.c for a complete example and includes files for fullscreen, cursor hiding, HiDPI, input events, timers, multiple windows, and individual platforms.

Input, Escape handling, and synchronization behavior

MiniFB supports event callbacks for keyboard, mouse, and window lifecycle changes. It also offers window and input state queries, allowing an application to choose callback-driven handling or state inspection. Release v0.14.0 is described as bringing one input contract to every backend. This consistency matters because platform libraries traditionally expose different event conventions, but the README does not enumerate every event value or callback signature.

There is one default behavior that can surprise a new integration: releasing ESC closes the window. The next update then returns MFB_STATE_EXIT. Applications that want to keep the window open must install their own keyboard callback. Any implementation must still handle the exit state correctly because a different shutdown path can destroy the handle internally.

Synchronization is not merely a timer around identical presentation semantics. Wayland waits for a compositor frame callback inside mfb_update_ex(), which can block. If the window is minimized or hidden and no callback arrives, the call waits briefly and returns MFB_STATE_OK without presenting. Android can also return MFB_STATE_OK while ANativeWindow is temporarily unavailable during a lifecycle transition. Therefore, MFB_STATE_OK means that the frame was accepted, not that the current platform state guarantees visible pixels. Frame pacing applications should account for those documented exceptions rather than treating every successful return as proof of display.

Backend flags and reduced platform coverage

The flag table is a central integration constraint. Windows, X11, and macOS advertise support for resizing, borderless operation, always-on-top behavior, and two fullscreen modes, but some entries are best effort. X11 borderless, always-on-top, and fullscreen behavior depends on the window manager or compositor. Wayland ignores MFB_WF_ALWAYS_ON_TOP and emits a warning. Android and iOS ignore all five listed decoration flags. DOS ignores them as well, so its examples and reduced API should not be treated as equivalent to a desktop windowing environment.

The two fullscreen flags also have a defined precedence. MFB_WF_FULLSCREEN takes priority when both MFB_WF_FULLSCREEN and MFB_WF_FULLSCREEN_DESKTOP are supplied. Backend behavior behind those names still differs. On Wayland, fullscreen desktop is implemented as maximization, and macOS uses zoom or maximize semantics. In a browser, fullscreen is managed by the page and generally requires a user gesture. The Web canvas also follows its CSS layout box scaled by devicePixelRatio, which introduces page layout requirements that do not exist in the same form for a native desktop window.

iOS and Android provide a single window with touch input. WebAssembly is partial, with some calls accepted but not applied. DOS provides a single window and partial keyboard support. These distinctions matter more than the simple cross-platform label: the backend selected on a target does not necessarily implement every operation offered by the common interface.

Pixel and presentation constraints for MiniFB

MiniFB does not convert pixel data. The documented model expects 32-bit pixel values, but the README does not establish a universal byte order or channel layout across every target. That omission matters when a developer writes values directly through uint32_t or reuses assets created for a different native format. The project states that it does not work around systems that cannot provide a proper 32-bit display. An application must therefore validate the pixel representation for its chosen environment rather than assuming that any uint32_t can be copied everywhere unchanged.

Resize handling is similarly caller-driven in the documented loop. The buffer dimensions passed to mfb_update_ex() must match the buffer the caller maintains. The API exposes resize callbacks and viewport control, but the example does not resize or reallocate the pixel array automatically. A production renderer needs to decide whether to allocate for a maximum size, rebuild its buffer after a resize event, or use viewport information to restrict drawing.

Wayland and Android add further constraints around presentation readiness. Wayland can return success without presenting when hidden or minimized. Android can do so during transitions in which ANativeWindow is unavailable. These are not pixel-format failures, but they affect visual verification and test design. Automated or manual checks should distinguish accepted input, compositor readiness, and actual visibility rather than assuming the return code alone proves that a frame appeared.

Maintenance, upgrades, and the alternative design

The repository is not archived. Its last push was on 2026-10-14, and the listed releases include v0.15.0 on 2026-10-12, v0.14.0 on 2026-09-24, and v0.13.0 on 2026-08-30. The release summaries identify title retrieval and memory-checking options in v0.15.0, a shared input contract in v0.14.0, and client-side window decorations on Wayland in v0.13.0. Those dates and documented changes provide concrete upgrade checkpoints. They do not remove the need to review CHANGELOG.md and platform notes when changing backends or event handling.

MiniFB is licensed under MIT. For source reuse and distribution, the practical implication is that the license text and its conditions travel with the code. The repository does not replace review of linked system libraries or application dependencies. The license identifier should also be retained when MiniFB is vendored or incorporated into a larger distribution.

The closest architectural alternative is not another small window wrapper described by the README. It is to use a rendering framework or a platform graphics API that owns surfaces, formats, presentation, and input integration. Such a stack adds more structure but can handle pixel conversion, backend capabilities, and resource management outside the application. MiniFB takes the opposite approach: fewer abstractions, a caller-owned buffer, and explicit platform behavior. The trade-off is direct. MiniFB can fit a small existing draw loop, while an engine is more appropriate when the application needs managed graphics resources and a broader feature set.

Editorial conclusion

Adopt MiniFB when the application already owns its rendering loop, accepts a hand-managed 32-bit buffer, and only needs the documented window and input operations. Do not choose it for a full engine, unsupported decoration flags, or environments without a suitable 32-bit display. Before integration, verify the exact backend feature table, pixel layout assumptions, lifecycle handling, and the synchronization behavior of mfb_update_ex().

Frequently asked questions

How do I install MiniFB?

The README links to platform-specific build instructions for Windows, X11, Wayland, macOS, iOS, Android, WebAssembly, and DOS. It does not provide the actual build command, so installation must follow the relevant section and CMakeLists.txt.

Does MiniFB allocate the frame buffer?

No. The application allocates the 32-bit pixel buffer and must keep it valid while calling mfb_update() or mfb_update_ex(). MiniFB copies the buffer to the window but does not allocate or free it.

Why does my MiniFB window close when I release Escape?

The default behavior treats releasing ESC as a request to close the window. The next update returns MFB_STATE_EXIT. Install a custom keyboard callback if the application should handle ESC without using the default close behavior.

Does MFB_STATE_OK always mean that my MiniFB frame is visible?

No. Wayland can return MFB_STATE_OK without presenting while a minimized or hidden window is waiting for a compositor frame callback. Android can also return it while ANativeWindow is temporarily unavailable during a lifecycle transition.

Which window flags work on Android and iOS?

The backend table marks MFB_WF_RESIZABLE, MFB_WF_BORDERLESS, MFB_WF_ALWAYS_ON_TOP, MFB_WF_FULLSCREEN, and MFB_WF_FULLSCREEN_DESKTOP as ignored for Android and iOS. Those platforms provide a reduced API centered on a single window and touch input.

Can MiniFB draw text or shapes for me?

No. The application draws everything into its own 32-bit pixel buffer before calling an update function. MiniFB handles the window, pixel transfer, synchronization, and documented input operations, but it does not provide a rendering API.

Official sources

  1. emoon/minifb on GitHub
  2. Issues
  3. License: MIT
  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/emoon-minifb.svg)](https://hysenlabs.com/projects/emoon-minifb)

Was this useful?