Display Configuration
Cuttlefish UI talks to physical displays through a display block in your cuttlefish.config.ts. You pick a built-in display profile (or name a driver) and tell Cuttlefish which pins it’s wired to; when you build, it generates the right driver code for you.
This page covers the display wizard, the common profiles, the full list of configuration fields, touch input, and — for unusual displays — how to wire up a brand-new driver.
Display wizard
The wizard asks which display you have and how it is wired, then writes the settings for you. It is the fastest way to get a screen running.
Run it from your project directory:
npx @typecad/ui --configIf you do not have a project yet, create one first with npx @typecad/cuttlefish create.
The wizard asks which display you are using, which pins it is connected to, how the screen is rotated, whether to smooth text edges (antialiasing), and how touch is wired. Each question suggests the common answer — press Enter to accept it.
Before anything is written, the wizard shows you the finished settings and asks you to confirm. It then does three things:
- Writes the display settings into
cuttlefish.config.ts. The rest of the file is left alone. - Adds a
previewscript topackage.json, sonpm run previewshows your screen in a browser. - Creates a starter screen file if you do not have one yet.
The wizard warns you if the display and the touch panel are set to the same pin.
You can run the wizard again at any time. Your previous answers are filled in, and nothing is changed without your confirmation.
Minimal SPI example (ST7796S TFT)
import type { CuttlefishConfig } from '@typecad/cuttlefish/api';
const config: CuttlefishConfig = {
entry: './src/app.ui',
target: 'esp32s3',
board: 'esp32s3_devkitc/esp32s3/procpu',
framework: '@typecad/framework-zephyr',
frameworkData: { buildTarget: 'esp32s3_devkitc/esp32s3/procpu' },
output: { framework: 'zephyr', outDir: './out' },
display: {
profile: 'st7796-zephyr',
cs: 5,
dc: 17,
rst: 16,
spiFrequency: 80000000,
colorOrder: 'bgr',
invertDisplay: false,
antialias: true,
},
};
export default config;Minimal mono example (SSD1306 OLED)
display: {
profile: 'ssd1306-zephyr',
width: 128,
height: 64,
rotation: 0,
}The mono profile drives direct display.* drawing — the HTML/CSS UI adapter does not lower to 1-bit panels.
Profile fields
| Field | Type | Description |
|---|---|---|
profile | string | Built-in profile name (e.g. "st7796-zephyr") — fills in driver, width, height, colorFormat, rotation |
driver | string | Display driver id (e.g. "ili9341", or a Zephyr id like "ili9341-zephyr") — set explicitly to override the profile |
width / height | number | Visible layout dimensions in pixels (after rotation) |
nativeWidth / nativeHeight | number | Native panel dimensions before rotation, when they differ from the layout size |
colorFormat | "rgb565" | "rgb888" | "mono" | Color depth |
displayClass | "tft" | "eink" | "oled" | Panel class (drives refresh behavior) |
capabilities | object | Declared adapter capabilities (e.g. partial refresh) |
rotation | number | 0 = portrait, 1 = landscape, 2-3 = inverted |
backlightPin | number | Zephyr: backlight GPIO — emits a gpio-leds DT node + backlight alias. Omit when the backlight is hardwired to power |
cs / dc / rst | number | SPI wiring pins |
bus | string | Bus identifier ("SPI" or "I2C") |
address | number | I2C address (I2C displays) |
reset | number | Reset pin for I2C displays (-1 if unwired) |
spiFrequency | number | SPI frequency override |
spiPins | { mosi, sck, miso } | Explicit SPI wiring (Zephyr DT overlay) |
colorOrder | "rgb" | "bgr" | Pixel color order |
invertDisplay | boolean | Invert panel colors |
antialias | boolean | Enable text antialiasing |
scanlineSync | boolean | Opt in to experimental GET_SCANLINE dirty-rectangle sync. Off by default — some ST7796S modules misbehave when reading that command |
fullscreen | boolean | "desktop" | SDL desktop only: borderless fullscreen |
title / icon | string | SDL desktop only: window title / taskbar icon (BMP) |
themeCss | string | Path to an extra CSS file with theme rules |
themeClass | string | Class name selecting a build-time theme variant |
scroll | ScrollConfig | Scroll engine tuning — see below |
touch | object | Touch-input config (see below) |
Scroll tuning (scroll)
All fields optional — defaults derive from the declared touch hardware:
| Field | Type | Description |
|---|---|---|
inputTier | "capacitive" | "resistive" | "none" | Input class |
renderTier | "full" | "constrained" | Whether per-frame canvas repaint (incl. rubber-band) is affordable |
dragScale | number | Multiplier on drag deltas (default 1.0) |
maxOverscroll | number | Rubber-band excursion ceiling in px (default 40) |
stiffness | number | Rubber-band resistance (higher = stiffer, default 0.5) |
edgeSnapPx | number | Free snap distance to a boundary (default 12) |
inputSmoothing | number | Low-pass coefficient for resistive input (default 0.3; capacitive always passthrough) |
Built-in profiles
| Profile | Dimensions | Color | Notes |
|---|---|---|---|
st7796-zephyr | 320×480 | RGB565 | ST7796S SPI TFT; hardware-tuned; add touch via touch config |
ili9341-zephyr | 320×240 | RGB565 | ILI9341 SPI TFT; UI adapter ported, not yet hardware-verified |
ssd1306-zephyr | 128×64 | Mono | Direct display.* ops only (no HTML/CSS UI adapter) |
Registered display drivers
Beyond the built-in profiles, the transpiler registers drivers you can target directly by setting driver (and width/height/colorFormat):
| Driver | Targets | Status |
|---|---|---|
st7796-zephyr | ST7796S SPI TFT (RGB565) | ✅ direct SPI¹ — hardware-tuned |
ili9341-zephyr | ILI9341 SPI TFT (RGB565) | ✅ direct SPI¹ — UI adapter ported, not yet hardware-verified |
ssd1306-zephyr | SSD1306 OLED (128×64 mono) | ✅ direct display.* ops² |
sdl | Native desktop window (RGB888) | ✅ Native framework only |
The Zephyr framework generates native display/touch adapters — no third-party display library dependency. The display devicetree overlay, pinctrl remux, DMA, and backlight alias, plus the matching prj.conf Kconfig symbols (CONFIG_DISPLAY, CONFIG_SPI, CONFIG_I2C, CONFIG_DMA), are generated from a usage scan of your code. See Zephyr framework for details.
¹ The ST7796S and ILI9341 are driven over raw spi_write() with manual CS/DC/RST GPIO — the adapter deliberately bypasses Zephyr’s mipi-dbi-spi bridge and disables the in-tree panel drivers (CONFIG_ST7796S / CONFIG_ILI9341).
² The SSD1306 profile uses a page framebuffer under the GFX runtime for direct display.* drawing; the @typecad/ui HTML/CSS adapter does not lower to mono displays.
Touch input
Add a touch block to enable touch. Two controllers have native adapters, both polled directly (no in-tree driver): FT6336U (capacitive, over I2C) and XPT2046 (resistive, over SPI). Controllers not listed here need a custom adapter (below).
FT6336U (I2C capacitive)
touch: {
library: 'FT6336U',
i2cAddress: 0x38, // default 0x38
i2cFrequency: 400000, // default 400 kHz
sda: 8, // I2C wiring — configures the bus pinctrl
scl: 9, // in the devicetree overlay
resetPin: 4, // hardware-reset GPIO
irq: 15, // optional (polled; not required for detection)
calibration: { xMin: 0, xMax: 320, yMin: 0, yMax: 480 },
}XPT2046 (SPI resistive)
touch: {
library: 'XPT2046_Touchscreen',
cs: 14, // touch chip-select (separate from display CS)
irq: 2, // optional
calibration: { xMin: 200, xMax: 3900, yMin: 240, yMax: 3800 },
}⚙️ Legacy touch controllers
4-wire analog resistive, STMPE610, GT911, and CST816S shipped with the removed Arduino framework’s touch adapters; the Zephyr framework has no native adapter for them. Other controllers need a custom adapter:
touch: {
adapter: './my-touch-adapter',
calibration: { xMin, xMax, yMin, yMax },
minPressure: 10,
}// legacy analog-resistive shape (Arduino era)
touch: {
library: 'Adafruit_TouchScreen',
analogPins: { xp, yp, xm, ym, rx },
calibration: { xMin, xMax, yMin, yMax },
}Custom adapter
touch: {
adapter: './my-touch-adapter',
calibration: { xMin, xMax, yMin, yMax },
}Calibration
calibration maps raw touch-controller readings to display pixels: { xMin, xMax, yMin, yMax }. minPressure filters out feather-light touches (resistive controllers only; capacitive controllers skip this gate).
Touch config fields
| Field | Type | Description |
|---|---|---|
library | string | Controller library name (FT6336U or XPT2046_Touchscreen) |
i2cAddress | number | I2C address (FT6336U: 0x38) |
i2cFrequency | number | I2C clock speed in Hz (default 400000) |
sda / scl | number | I2C wiring — configures the bus pinctrl in the devicetree overlay |
cs | number | SPI chip-select (XPT2046) |
resetPin | number | Hardware-reset GPIO |
irq | number | Interrupt-request pin (polled; not required for detection) |
calibration | object | { xMin, xMax, yMin, yMax } — raw-to-pixel mapping (required) |
⚙️ Advanced details — writing a new display driver
For drivers not in the built-in set, register an adapter at build time. The runtime contract every adapter must satisfy is the core display API:
display_init()display_fillScreen(color)display_startWrite()/display_endWrite()display_setAddrWindow(x, y, w, h)display_writePixels(pixels, count)display_partial_refresh(x, y, w, h)
Register with registerDisplayAdapter(driver, generator) and provide a DisplayProfile so the transpiler knows the dimensions and color format. For the generator interface, see the display-adapter source in the @typecad/cuttlefish package.
Next: Authoring API for the ui.* functions and the .ui file format.
On This Page