Cronus Zen Guide
GPC Scripting 3 min read

GPC feedback: drawing on the Cronus Zen OLED and using controller LEDs and rumble

How a Cronus Zen script tells the player what it is doing: the 128 by 64 OLED functions (cls_oled, print, rect_oled, puts_oled), the dirty-flag redraw pattern, controller LED states with set_led and set_ledx, DualShock 4 light bar colours, rumble overrides, and returning control to the console.

Updated
GPC script code on a dark editor

Quick answer

A script can signal state three ways: draw on the 128 by 64 OLED with cls_oled, rect_oled and puts_oled; set controller LEDs with set_led or set_ledx for a finite blink count; and pulse rumble with set_rumble on a 0 to 100 scale. Draw only on change, and call reset_leds and reset_rumble afterwards.

Why does feedback matter in a script?

A script with a toggle and no indicator is a guessing game. Did the press register? Is the mode on? The Zen gives you three channels that cost nothing in game terms: the OLED on the device, the LEDs on the controller, and rumble. Used sparingly they make a script feel finished. Used carelessly they slow the script down or leave the controller blinking after the feature has ended.

The OLED: what you have to work with

The display is 128 × 64 monochrome pixels, X from 0 to 127 and Y from 0 to 63. The built-ins:

FunctionPurpose
cls_oled(color)Fill the screen with OLED_BLACK or OLED_WHITE
pixel_oled(x, y, color)One pixel
line_oled(x, y, to_x, to_y, thickness, color)Line, thickness 1 to 127
rect_oled(x, y, width, height, fill, color)Rectangle, outline or filled
circle_oled(x, y, radius, fill, color)Circle, outline or filled
print(x, y, font, color, string_address)Draw a const string
putc_oled(position, ascii_code) / puts_oled(x, y, font, length, color)Build a character buffer of up to 18 characters, then draw it
image_oled(...)Draw a packed monochrome image

Fonts are OLED_FONT_SMALL, OLED_FONT_MEDIUM and OLED_FONT_LARGE.

The pattern: draw only when dirty

The official example is the one to copy:

gpc
const string READY_TEXT = "READY";
int oled_dirty = TRUE;

main {
    if(event_press(PS5_TRIANGLE)) oled_dirty = TRUE;

    if(oled_dirty) {
        cls_oled(OLED_BLACK);
        rect_oled(4, 4, 120, 56, FALSE, OLED_WHITE);
        print(36, 23, OLED_FONT_MEDIUM, OLED_WHITE, addr(READY_TEXT));
        oled_dirty = FALSE;
    }
}

oled_dirty starts true so the screen is drawn once at load. Anything that changes what should be shown sets it true again. The draw block runs only on those cycles. The official reference is explicit: repeated full-screen work in every VM cycle can make the script unresponsive.

Showing a value that changes

For numbers, use the character buffer. putc_oled places ASCII codes at buffer positions 1 to 18, and puts_oled draws the buffer. A common helper converts a two-digit value:

gpc
int strength = 25;
int oled_dirty = TRUE;

main {
    if(event_press(PS5_UP)) {
        strength = strength + 1;
        if(strength > 99) strength = 99;
        oled_dirty = TRUE;
    }
    if(oled_dirty) {
        cls_oled(OLED_BLACK);
        putc_oled(1, 48 + strength / 10);
        putc_oled(2, 48 + strength % 10);
        puts_oled(52, 24, OLED_FONT_LARGE, 2, OLED_WHITE);
        oled_dirty = FALSE;
    }
}

48 is the ASCII code for 0, so 48 + digit gives the character. This is exactly the kind of code that belongs behind a dirty flag: it is cheap once and expensive every cycle.

Controller LEDs

The controller's player LEDs are addressable as LED_1 onward. States are 0..3: off, on, fast blink, slow blink.

gpc
init {
    set_led(LED_1, 1);          // on
}

main {
    if(event_press(PS5_CROSS)) {
        set_ledx(LED_1, 3);     // blink three times, then stop
    }
    if(event_press(PS5_CIRCLE)) {
        reset_leds();           // give control back to the console
    }
}

set_ledx runs a finite blink sequence; get_ledx() tells you whether one is still active, and a blink count of zero leaves the LED on. get_led(LED_n) reads the current state. The important habit is reset_leds() when your feature turns off. A script that leaves an LED blinking forever is the most common feedback bug.

DualShock 4 light bar

On a DualShock 4 you have a full RGB light bar. Channel values are 0..255.

gpc
main {
    if(get_ival(PS4_L2)) {
        set_ps4_lbar(255, 0, 0);    // red while L2 held
    }
}

get_ps4_lbar(PS4_GREEN) and the other channel selectors read what the console is sending, useful when a game uses the light bar for health or team colour and you want to restore it. Firmware 2.3.2 made light bar and rumble behaviour consistent between USB and Bluetooth, so this works the same either way on current firmware.

Rumble as confirmation

A short rumble on a toggle is unmistakable and needs no glance at the screen. Strengths are 0..100.

gpc
combo Confirm {
    set_rumble(RUMBLE_A, 60);
    wait(120);
    reset_rumble();
}

main {
    if(event_press(PS5_OPTIONS)) combo_run(Confirm);
}

reset_rumble() clears your override and returns control to the console; block_rumble() in init suppresses console rumble entirely if a script needs a quiet controller. The official page's advice is to reset explicitly on feature exit so a manual output cannot remain active. Combos are covered in combos and wait timing.

A feedback checklist

  1. One owner per output. Decide which part of the script draws the OLED, sets LEDs and fires rumble. The official architecture guide calls this the feedback ownership pattern.
  2. Dirty flags for the OLED. Always.
  3. Reset on exit. reset_leds() and reset_rumble() when a mode turns off.
  4. Short and rare rumble. It is confirmation, not decoration.
  5. Readable text. Two or three lines in medium or large font beat a dense small-font panel.
  6. Test with a GamePack in another slot to see how Collective Minds' own packs present state; they set the house style.

Where this fits

Feedback is the last of the four building blocks. Input reading is in get_val and stick values, timing in combos and wait, and memory in SPVAR persistence. Put them together and you have the structure of every GamePack on the device.

Frequently asked questions

Why does my script lag when I draw on the OLED?
Because it redraws every cycle. The official guide warns that repeated full-screen work in every VM cycle can make the script unresponsive. Keep a dirty flag, set it when a value changes, and draw only when it is set.
What fonts and sizes are available?
OLED_FONT_SMALL, OLED_FONT_MEDIUM and OLED_FONT_LARGE. Coordinates run X 0 to 127 and Y 0 to 63. The print built-in draws a const string; putc_oled and puts_oled build and draw a buffer of up to 18 characters.
What LED states exist?
Four, numbered 0 to 3: off, on, fast blink and slow blink, set with set_led. set_ledx blinks a finite number of times, and get_ledx reports whether such a sequence is still running. reset_leds returns LED control to the console.
Can I change the DualShock 4 light bar colour?
Yes, with set_ps4_lbar(red, green, blue) using 0 to 255 per channel, and get_ps4_lbar to read the console's own colour. Rumble uses a 0 to 100 scale via set_rumble and reset_rumble.

Sources

Every link was live when this guide was last updated.

  1. 01OLED drawing and text (official GPC guide)guide.cronuszen.com
  2. 02OLED display built-ins reference (official GPC guide)guide.cronuszen.com
  3. 03Controller LEDs (official GPC guide)guide.cronuszen.com
  4. 04DualShock 4 lightbar (official GPC guide)guide.cronuszen.com
  5. 05Rumble and haptics (official GPC guide)guide.cronuszen.com

This guide is independent and unofficial; product names belong to their owners. Found an error? Use the contact page and we will correct it and note the change.