Skip to content

Quantum Painter

Quantum Painter is the standardised API for graphical displays. It currently includes support for basic drawing primitives, as well as custom images, animations, and fonts.

Due to the complexity, there is no support for Quantum Painter on AVR-based boards.

To enable overall Quantum Painter to be built into your firmware, add the following to


You will also likely need to select an appropriate driver in, which is listed below.


Quantum Painter is not currently integrated with system-level operations such as when the keyboard goes into suspend. Users will need to handle this manually at the current time.

The QMK CLI can be used to convert from normal images such as PNG files or animated GIFs, as well as fonts from TTF files.

Supported devices:

Display PanelPanel TypeSizeComms TransportDriver
GC9A01RGB LCD (circular)240x240SPI + D/C + RSTQUANTUM_PAINTER_DRIVERS += gc9a01_spi
ST7735RGB LCD132x162, 80x160SPI + D/C + RSTQUANTUM_PAINTER_DRIVERS += st7735_spi
ST7789RGB LCD240x320, 240x240SPI + D/C + RSTQUANTUM_PAINTER_DRIVERS += st7789_spi
SH1106 (SPI)Monochrome OLED128x64SPI + D/C + RSTQUANTUM_PAINTER_DRIVERS += sh1106_spi
SH1106 (I2C)Monochrome OLED128x64I2CQUANTUM_PAINTER_DRIVERS += sh1106_i2c
SSD1306 (SPI)Monochrome OLED128x64SPI + D/C + RSTQUANTUM_PAINTER_DRIVERS += sh1106_spi
SSD1306 (I2C)Monochrome OLED128x32I2CQUANTUM_PAINTER_DRIVERS += sh1106_i2c
SurfaceVirtualUser-definedNoneQUANTUM_PAINTER_DRIVERS += surface

Quantum Painter Configuration

QUANTUM_PAINTER_DISPLAY_TIMEOUT30000This controls the amount of time (in milliseconds) that all displays will remain on after the last user input. If set to 0, the display will remain on indefinitely.
QUANTUM_PAINTER_TASK_THROTTLE1This controls the amount of time (in milliseconds) that the Quantum Painter internal task will wait between each execution. Affects animations, display timeout, and LVGL timing if enabled.
QUANTUM_PAINTER_NUM_IMAGES8The maximum number of images/animations that can be loaded at any one time.
QUANTUM_PAINTER_NUM_FONTS4The maximum number of fonts that can be loaded at any one time.
QUANTUM_PAINTER_CONCURRENT_ANIMATIONS4The maximum number of animations that can be executed at the same time.
QUANTUM_PAINTER_LOAD_FONTS_TO_RAMFALSEWhether or not fonts should be loaded to RAM. Relevant for fonts stored in off-chip persistent storage, such as external flash.
QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE1024The limit of the amount of pixel data that can be transmitted in one transaction to the display. Higher values require more RAM on the MCU.
QUANTUM_PAINTER_SUPPORTS_256_PALETTEFALSEIf 256-color palettes are supported. Requires significantly more RAM on the MCU.
QUANTUM_PAINTER_SUPPORTS_NATIVE_COLORSFALSEIf native color range is supported. Requires significantly more RAM on the MCU.
QUANTUM_PAINTER_DEBUGunsetPrints out significant amounts of debugging information to CONSOLE output. Significant performance degradation, use only for debugging.
QUANTUM_PAINTER_DEBUG_ENABLE_FLUSH_TASK_OUTPUTunsetBy default, debug output is disabled while the internal task is flushing the display(s). If you want to keep it enabled, add this to your config.h. Note: Console will get clogged.

Drivers have their own set of configurable options, and are described in their respective sections.

Quantum Painter CLI Commands

This command converts images to a format usable by QMK, i.e. the QGF File Format.


usage: qmk painter-convert-graphics [-h] [-w] [-d] [-r] -f FORMAT [-o OUTPUT] -i INPUT [-v]

  -h, --help            show this help message and exit
  -w, --raw             Writes out the QGF file as raw data instead of c/h combo.
  -d, --no-deltas       Disables the use of delta frames when encoding animations.
  -r, --no-rle          Disables the use of RLE when encoding images.
  -f FORMAT, --format FORMAT
                        Output format, valid types: rgb888, rgb565, pal256, pal16, pal4, pal2, mono256, mono16, mono4, mono2
  -o OUTPUT, --output OUTPUT
                        Specify output directory. Defaults to same directory as input.
  -i INPUT, --input INPUT
                        Specify input graphic file.
  -v, --verbose         Turns on verbose output.

The INPUT argument can be any image file loadable by Python's Pillow module. Common formats include PNG, or Animated GIF.

The OUTPUT argument needs to be a directory, and will default to the same directory as the input argument.

The FORMAT argument can be any of the following:

rgb88816,777,216 colors in 8-8-8 RGB format (requires QUANTUM_PAINTER_SUPPORTS_NATIVE_COLORS)
rgb56565,536 colors in 5-6-5 RGB format (requires QUANTUM_PAINTER_SUPPORTS_NATIVE_COLORS)
pal256256-color palette (requires QUANTUM_PAINTER_SUPPORTS_256_PALETTE)
pal1616-color palette
pal44-color palette
pal22-color palette
mono256256-shade grayscale (requires QUANTUM_PAINTER_SUPPORTS_256_PALETTE)
mono1616-shade grayscale
mono44-shade grayscale
mono22-shade grayscale


$ cd /home/qmk/qmk_firmware/keyboards/my_keeb
$ qmk painter-convert-graphics -f mono16 -i my_image.gif -o ./generated/
Writing /home/qmk/qmk_firmware/keyboards/my_keeb/generated/my_image.qgf.h...
Writing /home/qmk/qmk_firmware/keyboards/my_keeb/generated/my_image.qgf.c...

Quantum Painter Display Drivers

Most TFT display panels use a 5-pin interface -- SPI SCK, SPI MOSI, SPI CS, D/C, and RST pins.

For these displays, QMK's spi_master must already be correctly configured for the platform you're building for.

The pin assignments for SPI CS, D/C, and RST are specified during device construction.

Enabling support for the GC9A01 in Quantum Painter is done by adding the following to


Creating a GC9A01 device in firmware can then be done with the following API:

painter_device_t qp_gc9a01_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode);

The device handle returned from the qp_gc9a01_make_spi_device function can be used to perform all other drawing operations.

The maximum number of displays can be configured by changing the following in your config.h (default is 1):

// 3 displays:
#define GC9A01_NUM_DEVICES 3

Native color format rgb565 is compatible with GC9A01

Quantum Painter Drawing API

All APIs require a painter_device_t object as their first parameter -- this object comes from the specific device initialisation, and instructions on creating it can be found in each driver's respective section.

To use any of the APIs, you need to include qp.h:

#include <qp.h>

The coordinate system used in Quantum Painter generally accepts left, top, right, and bottom instead of x/y/width/height, and each coordinate is inclusive of where pixels should be drawn. This is required as some datatypes used by display panels have a maximum value of 255 -- for any value or geometry extent that matches 256, this would be represented as a 0, instead.


Drawing a horizontal line 8 pixels long, starting from 4 pixels inside the left side of the display, will need left=4, right=11.

All color data matches the standard QMK HSV triplet definitions:

  • Hue is of the range 0...255 and is internally mapped to 0...360 degrees.
  • Saturation is of the range 0...255 and is internally mapped to 0...100% saturation.
  • Value is of the range 0...255 and is internally mapped to 0...100% brightness.


Colors used in Quantum Painter are not subject to the RGB lighting CIE curve, if it is enabled.