![]() |
HOME
–
BLOG
–
CONTACT
–
ABOUT
|
HOME / BRAWL
brawl:device/matrix
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.
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.
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;
}
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.
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.
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.
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.
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.
Constant | |
---|---|
MATRIX_MAX_FRAMES |
The maximum number of frames supported. |
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. |
//--------------------------------------------------------------------------
// 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
};
int32_t // MATRIX_ERROR|int32_t
matrixDimensions(void);
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);
int32_t // MATRIX_ERROR|void
matrixLoadPixels(int32_t mem_ptr, int32_t w, int32_t h);
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);
//--------------------------------------------------------------------------
#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;
}
2025-10 (draft)