Display module
Display opens the Windows window or the board's screen and presents on it what you draw. It also keeps the game's pace (fps and dt), the background of each frame and the view, the part of the screen where drawing happens. To draw, use the Draw module, which you have in Display.draw.

use Display
Platforms
| Windows | ESP32 / ESP32-S3 | ESP32-P4 | ESP32-S31 | |
|---|---|---|---|---|
| Screen size | The one in open(width, height) | The board's panel: open does not use its arguments | The panel's; open(width, height) sets a view of that size in the center | As on the P4 |
Colors with double buffer (mode(2)) | 24 bits per pixel | 8 bits per pixel: 256 colors | 16 bits per pixel | 16 bits per pixel |
orientation | Only counts before open: 0 and 2 swap width and height | Rotates the panel, after open | Rotates the panel and centers the view again | As on the P4 |
backlight | Does nothing | Turns the light on or off | Turns the light on or off | Turns the light on or off |
| Window size | From x1 to x4, with Window Scale | — | — | — |
update() | Also looks after the window: closing it ends the program | — | Presents at the panel's refresh rate | Presents at the panel's refresh rate |
The size of each panel, the view on the P4 and the S31 and how colors look on each board are in Display, sound, SD and network and in Memory and limits. Window Scale is in the project's Windows configuration.
Functions
| Function | What it does | Returns |
|---|---|---|
open(width, height) | Opens the window or the screen | true |
close() | Closes the screen and frees what was loaded | true |
update() | Presents the frame and prepares the next one; with fps, it waits | true, or the steps that are due |
mode() / mode(n) | Reads or changes the drawing mode | The mode / true |
orientation(n) | Rotates the screen | true |
backlight(on) | Turns the screen's light on or off | true |
loadBG(file) | Loads the background image of each frame | true |
loadTFT(file) / loadTFT(x, y, file) | Draws a fixed image outside the double buffer, like a frame | true |
With an argument of another type, the Display functions do not throw an exception: they return an error value and do nothing (see Errors and exceptions). The few that do throw say so in their section.
open
Opens the window (Windows) or the board's screen.
use Display
Display.open(320, 240)
println(Display.width, " x ", Display.height) // 320 x 240 on Windows
widthandheightare integers. With decimals it does not open: it returns an error value.- On Windows, the window measures
width × height, orheight × widthif you have calledorientation(0)ororientation(2)before. With Window Scale at x2, x3 or x4, the window looks bigger, but the program still seeswidth × height. - On the ESP32 and the S3, the size is the panel's, given by the board profile: the arguments are required, but they are not used.
- On the P4 and the S31, the size is also the panel's, and
width × heightis the view, centered on it (unless you have setviewWidthandviewHeightbefore).
After open, Display.width and Display.height tell the size of the screen, and viewWidth and viewHeight, the size of the view.
close
Closes the window or releases the screen. It frees the double buffer, the background image and the loadTFT frame, and sets showBG back to false, autoClear to true and the view to 0.
Display.close()
You can open it again with open afterwards. If the program goes on doing other things, close it to free that memory.
update
Presents what you have drawn since the previous update().
use Display
Display.open(320, 240)
Display.fps = 30 // the pace: 30 frames per second
var draw = Display.draw
for (var i = 0; i < 90; i++) // 3 seconds
draw.circle(160, 120, 10 + i)
Display.update() // presents, clears the view and waits
end
Display.close()
- In double buffer mode (the default),
update()presents the frame and prepares the next one: it clears it with the background color (ifautoClearis on) and draws the background image (ifshowBGis). Each frame starts clean: there is no need to calldraw.cls(). - In direct mode (
mode(1)), what you draw is already on the screen, andupdate()clears nothing. - With a pace (
Display.fps = N),update()waits until the next step is due and returns how many steps are due:1if the frame fit in its time,2or more if it was late (see fps). Without a pace, it returnstrue. - It measures the time between one
update()and the next: that isDisplay.dt, and also the clock that drives the sprites' animations and the physics of scenes. - On Windows,
update()also looks after the window: if you close it, the program ends there. A program that never callsupdate()cannot be closed from its window.
With arguments it does nothing and returns an error value.
mode
Reads or changes the drawing mode.
var currentMode = Display.mode() // 2, the default
Display.mode(1) // draw straight onto the screen
| Mode | What it does |
|---|---|
0 | Do not use it: it changes with the platform (on Windows, it does the same as 2). |
1 | Direct. Each Draw function draws on the screen at once. update() clears nothing and does not draw the background. What moves flickers. |
2 | Double buffer (the default). You draw on a frame that is not shown, and update() presents it in one go. Then it prepares the next one with autoClear and showBG. |
Double buffer is what suits a game. On the ESP32 and the S3 it takes memory (one byte per pixel of the view) and has fewer colors; direct mode does not use that memory. A value outside 0–2 does not change the mode: it returns an error value.
orientation
Rotates the screen.
Display.orientation(1) // landscape
| Value | Rotation | On Windows |
|---|---|---|
0 | 0 degrees | Portrait: the window measures height × width |
1 | 90 degrees | Landscape (the default) |
2 | 180 degrees | Portrait |
3 | 270 degrees | Landscape |
- Only the two lowest bits count:
4is0,5is1… - On Windows, it only counts before
open, to choose a portrait or a landscape window. It does not rotate what you draw. - On a board, it rotates the panel, and
Display.widthandDisplay.heightchange with it. Call it afteropen: before, it does nothing, and the screen starts with the rotation given by the board profile. On the P4 and the S31 it also centers the view again.
backlight
Turns the screen's light on or off.
Display.backlight(false) // turns the light off
Display.backlight(true) // turns it on
It takes true, false or a number: 0 turns it off, and any other number turns it on. On a board it does not set the brightness: the light is at full or off. On Windows it does nothing. It depends on the board profile saying which pin controls the light.
loadBG
Loads a BMP image as the background. With showBG set to true, update() draws it when it starts each frame, at the posBG position of the view.
Display.loadBG("Images/background.bmp")
Display.showBG = true // without this, the background is not drawn
Display.posBG = [0, 0]
- The path is the project's, as in
Bitmap.load: the image has to reach the game (Include in Build in the image editor). It reads the same formats as Bitmap. - It is only drawn in double buffer mode.
- If it cannot find the image, it returns
false, writes the error to the console (on a board, to the serial monitor) and leaves the previous background as it was.
loadTFT
Draws a BMP image straight onto the screen, outside the double buffer. It is meant for a frame or a fixed decoration around the view: since update() only redraws the view, what is outside it stays.
// A frame at (0, 0)
Display.loadTFT("Images/frame.bmp")
// An image at a position
Display.loadTFT(50, 100, "Images/logo.bmp")
xandyare integers: the top left corner, in screen coordinates (not view coordinates). Without them, the image goes at(0, 0).- With arguments that are not valid, it throws
ERR_TYPE_MISMATCHorERR_INVALID_ARG_NUMBER. If it cannot load the image, on a board it throwsERR_FILE_READ_FAIL, and on Windows it returns an error value.
On Windows, loadTFT does not use x or y: it stretches the image over the whole window. To make it look the same as on the board, make the frame the size of the screen.
Properties
| Property | Type | Default | Read / write |
|---|---|---|---|
width, height | integer | 320 and 240 until open; then, the screen size | Read only |
viewWidth, viewHeight | integer | 0 until open; then, the view size | Both (assign them before open) |
fps | integer, from 0 to 1000 | 0: no pace | Both |
dt | decimal, in seconds | 0 | Read only |
showBG | yes/no | false | Both |
autoClear | yes/no | true | Both |
posBG | list [x, y] | [0, 0] | Write only |
draw | the Draw module | — | Read only |
Properties are assigned with =: Display.fps = 30. Calling them like a function (Display.fps(30)) throws ERR_INVALID_OPERATION, and assigning width, height or draw throws ERR_ATTRIBUTE_NOT_FOUND.
posBG
The position of the background image inside the view, as a list [x, y]. Decimals are truncated.
Display.posBG = [10, 20]
It is write only: reading Display.posBG does not return the position, but an error value.
showBG
If it is true, update() draws the loadBG image when it prepares each frame. It is false by default.
Display.showBG = true
autoClear
If it is true, update() clears each new frame with the background color, draw.colorBG. It is true by default.
Display.autoClear = false // what you draw stays from one frame to the next
With false, what you have drawn is still there in the next frame: it suits a program that only redraws what changes. showBG and autoClear only count in double buffer mode (2); in direct mode, update() does not draw any background.
With autoClear and showBG, update() clears first and then draws the background image: what the image does not cover keeps the background color.
viewWidth
The width of the view: the part of the screen where Draw draws. Draw coordinates start at the view's top left corner, and what falls outside it is not shown. The view is centered on the screen.
use Display
// A 240 x 160 view in a 320 x 240 window
Display.viewWidth = 240
Display.viewHeight = 160
Display.open(320, 240)
println(Display.viewWidth) // 240
- Assign it before
open. Until then it is0, andopensets it to the screen size if you have not given it another one. - On Windows, the ESP32 and the S3, assigning it after
openchanges what you read, but not the view. On the P4 and the S31 the view does change, and it is centered again. - It is meant for leaving a frame around the game (with
loadTFT), or for making a game made for a small screen look the same on a big one. - On Windows, some Draw functions do not count from the view: see the Draw pitfalls.
viewHeight
The height of the view. It works like viewWidth.
Display.viewHeight = 160
fps
The fps property sets the game's pace: how many logic steps per second it runs, whatever the board and however long each frame takes. It is an integer from 0 to 1000; with 0 (the default) there is no pace and Display.update() never waits.
With Display.fps = N, each Display.update() does not return until the next step is due (1/N of a second after the previous one) and returns how many steps are due. A game that moves a fixed amount per frame no longer speeds up when the board draws faster (for example, when there is no scrolling).
Display.fps = 30 // 30 steps per second on every board
If the board cannot draw N frames per second, a game that only sets Display.fps runs slower during that time. To keep its speed, it can run the missing steps without drawing, using the value update() returns:
Display.fps = 30
var steps = 1
while (true)
while (steps > 1) // the missing steps, without drawing
Logic()
steps = steps - 1
end
Logic()
Render()
steps = Display.update() // 1, or 2 or more if the frame was late
end
- A value outside
0–1000stays at the limit, and decimals are truncated. With something that is not a number, it throwsERR_TYPE_MISMATCH.
More than 150 ms late (a load, a pause), there is no catch-up: the count starts again at that moment, so the game does not race ahead. Assigning fps also restarts the count.
These boards present each frame at their panel's refresh rate. The game still runs at the fps speed, but motion is only even when fps divides the panel refresh rate (on a 60 Hz panel, 30 or 60).
dt
The dt property (read only) gives the seconds elapsed between the previous Display.update() and the last one, measured by the VM in microseconds (after the fps wait, if any). It is meant for games that move by multiplying by time, instead of computing it with clock().
var dt = Display.dt
ship.x = ship.x + speed * dt
It is 0 until the second update(), and after a load or a pause it is at most 0.15. With Display.fps = N and the frame on time, it is about 1/N: at 30 fps, around 0.0333. Assigning it throws ERR_INVALID_OPERATION.
width
The screen width in pixels (read only).
var w = Display.width
println("Screen width: ", w)
It is 320 until open. Then, the width of the window or of the panel, and on a board it changes with orientation.
height
The screen height in pixels (read only).
var h = Display.height
println("Screen height: ", h)
It is 240 until open. Then, the height of the window or of the panel.
draw
The Draw module, which draws.
var draw = Display.draw
draw.color = 0xFF0000
draw.circle(100, 100, 50, true)
It is the same module you get with use Draw.
Complete example
A square crosses a 240 × 160 view centered in the window, at 80 pixels per second, the same on every board.
use Display
// A 240 x 160 view in the center of a 320 x 240 window
Display.viewWidth = 240
Display.viewHeight = 160
Display.open(320, 240)
Display.fps = 30 // 30 steps per second, on Windows and on the boards
var draw = Display.draw
draw.colorBG = 0x102040 // the color update() clears the view with
var x = 0.0
var speed = 80 // pixels per second
for (var i = 0; i < 300; i++) // 10 seconds
draw.color = 0xFFCC00
draw.rect(x, 70, 20, 20, true)
Display.update() // presents, clears the view and waits for the next step
x = x + speed * Display.dt
if (x > Display.viewWidth) then
x = -20.0
end
end
Display.close()
Pitfalls and known issues
- On Windows, the view does not clip until the first
update(). What you draw outside the view before the firstupdate()shows. - On a board,
openmay not open the screen. If the board profile does not describe its screen correctly,openwrites “Display: Invalid panel/bus configuration from boot.cfg.” to the serial monitor and returns an error value. Check the profile: Board profiles. - On Windows,
draw.line,draw.circle,draw.polygonanddraw.getPixeldo not count from the view. With a view smaller than the screen, they come out shifted. See Draw. - On Windows,
loadTFTstretches the image over the whole window (see loadTFT).
All the known issues of JARU, together, are in Known issues.