LED matrix API reference

brawl:device/matrix

All device APIs

Overview

The brawl:device/matrix interface defines a portable, minimal LED Matrix API for WebAssembly (WASM) modules. It provides a consistent way to control LED Matrix arrays across diverse embedded targets - ranging from microcontrollers to emulators - while remaining fully compatible with the WASM MVP specification.

Each function follows a simple convention: it returns a signed 32-bit integer, where negative values represent standardized error codes and non-negative values represent valid results or success indicators. This design keeps the ABI deterministic, low-overhead, and portable between environments, enabling WASM applications to interact directly with LED Matrix devices without callbacks, threads, or host-specific extensions.

Implementation Notes

This section outlines the expected behavior and conventions used by the brawl:device/matrix interface. It provides guidance for both host implementers and guest developers to ensure consistent behavior across WASM targets. The underlying implementation (LED Matrix driver) is abstracted from the caller and managed by the host driver.

LED Matrix Dimensions

matrixDimensions() returns the width and height of the LED Matrix array packed into an i32. The high word contains the width whilst the lower word contains the height. To extract the width and height can be done as shown below:

int32_t dims = matrixDimensions();
if (dims >= 0) {
    int32_t width  = (dims >> 16) & 0xFFFF;
    int32_t height =  dims        & 0xFFFF;
}

Operation

To initialize and begin using the LED Matrix, simply call the function matrixInit() - this will initialize the underlying LED Matrix controller and configure background services required for its operation on the host environment. This function should be called before utilizing any other functions, or an MATRIX_ERROR_NOT_INITIALIZED error will be returned.

Individual LED Control

To change the state of individual LED elements within the LED Matrix, use the functions matrixOn() and matrixOff() to toggle between on and off. To clear and to stop any animations on the LED Matrix, simply call the matrixClear() function.

Loading Pixels

The matrixLoadPixels(mem_ptr, width, height) function takes an array of uint8_t elements, where a non zero value represents a pixel being ON and a zero value being OFF. The width and height should match the dimensions of the LED Matrix, on success - the host renders the LED Matrix immediately and stops any current animation.

uint8_t pixels[] = {
    0, 0, 0, 1, 1, 0, 0, 0,
    0, 0, 1, 0, 0, 1, 0, 0,
    0, 1, 0, 0, 0, 0, 1, 0,
    0, 1, 0, 0, 0, 0, 1, 0,
    0, 1, 1, 1, 1, 1, 1, 0,
    0, 1, 0, 0, 0, 0, 1, 0,
    0, 1, 0, 0, 0, 0, 1, 0,
    0, 0, 0, 0, 0, 0, 0, 0
};

matrixInit();
matrixClear();
matrixLoadPixels((int32_t)(uintptr_t)pixels, 8, 8);

The above example (pseudo code) will load the glyph A to an device that provides 8x8 LED Matrix.

Animation Frames

The matrixLoadFrames(mem_ptr, count) reads back-to-back frames from guest memory and transforms as required to the host memory requirements. Each frame consists of bitmap data (packed uint32_t words, row-major, padded to 32-bits), followed by a uint32_t value representing the duration in milliseconds between frames.

Lets consider the 'A' glyph shown earler, and how this would map into packed data (pseudo code).

//  0, 0, 0, 1, 1, 0, 0, 0  == 0x18
//  0, 0, 1, 0, 0, 1, 0, 0  == 0x24
//  0, 1, 0, 0, 0, 0, 1, 0  == 0x42
//  0, 1, 0, 0, 0, 0, 1, 0  == 0x42  // first uint32_t
//
//  0, 1, 1, 1, 1, 1, 1, 0  == 0x7E
//  0, 1, 0, 0, 0, 0, 1, 0  == 0x42
//  0, 1, 0, 0, 0, 0, 1, 0  == 0x42
//  0, 0, 0, 0, 0, 0, 0, 0  == 0x00  // second uint32_t

uint32_t frame_A[] = {
    0x18244242, 0x7E424200, 100      // 8x8 pixel data, 100 ms duration
};

matrixInit();
matrixLoadFrames((int32_t)(uintptr_t)frame_A, 1);

If more than one frame is defined, playback is initiated by calling matrixPlay(loop), a non zero value will loop the playback of the frames. Individual frames can be loaded via matrixRenderFrame() or move between frames using matrixNext(). At any time, the status of the sequence can be checked via matrixSequenceDone() that will return a non zero value when idle or finished and zero if an animation sequence is still active.

Error Codes

All brawl:device/matrix functions return a signed 32-bit integer (int32_t), which encodes both result and error information:

Value
ret < 0 an error occurred.
ret == 0 operation completed successfully (or no value to return).
ret > 0 operation succeeded and returned a value (e.g., packed dimensions or sequence state).

All memory access operations are bounds-checked by the host; going outside will return a MATRIX_ERROR_MEMORY_OOB error. All coordinate or index operations are bounds-checked by the host; invalid coordinates return a MATRIX_ERROR_INVALID_ARG error.

Constants

Constant
MATRIX_MAX_FRAMES The maximum number of frames supported.

Matrix Errors

All functions return a signed 32-bit integer (int32_t). If the value is negative, it represents an error code from the table below:

Constant
MATRIX_ERROR_NOT_INITIALIZED The LED Matrix module is not initiaized.
MATRIX_ERROR_INVALID_ARG The argument provided was invalid for the operation requested.
MATRIX_ERROR_MEMORY_OOB The requested operation would result in a MEMORY_OOB Error.

Application Programmer Interface (API)

//--------------------------------------------------------------------------
// brawl:device/matrix

#define MATRIX_MAX_FRAMES  16

// MATRIX errors
enum MATRIX_ERROR {
                           // >= 0           no error
  MATRIX_ERROR_NOT_INITIALIZED = -1,      // LED Matrix not initialized
  MATRIX_ERROR_INVALID_ARG     = -2,      // invalid argument
  MATRIX_ERROR_MEMORY_OOB      = -3       // memory out of bounds
};

LED Matrix Dimensions

int32_t // MATRIX_ERROR|int32_t
matrixDimensions(void);

Operation

int32_t // MATRIX_ERROR|void
matrixInit(void);

int32_t // MATRIX_ERROR|void
matrixOn(int32_t x, int32_t y);

int32_t // MATRIX_ERROR|void
matrixOff(int32_t x, int32_t y);

int32_t // MATRIX_ERROR|void
matrixClear(void);

Loading Pixels

int32_t // MATRIX_ERROR|void
matrixLoadPixels(int32_t mem_ptr, int32_t w, int32_t h);

Animation Frames

int32_t // MATRIX_ERROR|void
matrixLoadFrames(int32_t mem_ptr, int32_t count);

int32_t // MATRIX_ERROR|void
matrixRenderFrame(int32_t index);

int32_t // MATRIX_ERROR|void
matrixPlay(int32_t loop);

int32_t // MATRIX_ERROR|void
matrixNext(void);

int32_t // MATRIX_ERROR|int32_t
matrixSequenceDone(void);

//--------------------------------------------------------------------------

Example - Light up Corners on LED Matrix (C)

#include <stdint.h>
#include <stdio.h>

#include "brawl-device-matrix.h"

static void matrix_err(int32_t rc, const char *what) {
    if (rc < 0) fprintf(stderr, "%s failed: %d\n", what, rc);
}

int main() {

    int32_t dims, width, height;
    int32_t rc;

    // get the matrix dimensions
    dims   = matrixDimensions();
    if (dims < 0) { matrix_err(dims, "matrixDimensions"); return 1; }
    width  = (dims >> 16) & 0xFFFF;
    height =  dims        & 0xFFFF;

    // initialize and clear the LED Matrix
    rc = matrixInit();
    if (rc < 0) { matrix_err(rc, "matrixInit"); return 1; }
    rc = matrixClear();
    if (rc < 0) { matrix_err(rc, "matrixClear"); return 1; }

    // highlight the corner LEDs (guard against 0-sized matrices)
    if ((width > 0) && (height > 0)) {
        rc = matrixOn(0,       0);
        if (rc < 0) { matrix_err(rc, "matrixOn"); return 1; }
        rc = matrixOn(width-1, 0);
        if (rc < 0) { matrix_err(rc, "matrixOn"); return 1; }
        rc = matrixOn(0,       height-1);
        if (rc < 0) { matrix_err(rc, "matrixOn"); return 1; }
        rc = matrixOn(width-1, height-1);
        if (rc < 0) { matrix_err(rc, "matrixOn"); return 1; }
    }

    return 0;
}

Revision

2025-10 (draft)