I have always enjoyed the classic paper desktop calendars, with a distinct theme like birds, or historical facts. When M5Stack released the PaperColor I knew it would be an obvious candidate for a desktop calendar for the AI era!
I wanted it to wake up every morning and create a unique piece of art based on that particular day.
The finished device can show a historical event, a work of art, a seasonal Danish bird, an invention, a deliberately questionable corporate motivational quote, or whatever other prompt I add through its settings page. The calendar chooses a subject based on a theme and a style, generates the artwork, reduces it to the six colors available on the panel, adds its own title and date, refreshes the display and returns to deep sleep.
It is essentially a very opinionated picture frame that briefly connects to Wi-Fi, employs two AIs, and then refuses to answer emails for the rest of the day.
The ideaThe solution was to split the image generation job into two AI stages:
- A text model interprets a short prompt (e.g. “select historical event that happened on this date") in the context of the current date and produces structured metadata plus a detailed English image prompt.
- An image model turns that detailed prompt into the base artwork.
The PaperColor then performs the deterministic work: fitting the image, enforcing the physical palette, composing the date/title overlays, refreshing the panel and managing power.
The complete prompt-to-PaperColor image flow
How the complete flow worksStep 1 - Wake up and establish the dateAt its scheduled wake, the ESP32-S3 gets the local date and connects briefly to Wi-Fi. If today's artwork is already cached, it reuses it; otherwise it starts a new generation.
Step 2 - Turn a short prompt into a detailed promptThe selected prompt is deliberately short and reusable. The program adds:
- The current local date
- The requested language for title and description
- The display orientation and aspect ratio
- The six-color palette constraint
- Composition guidance for a small e-paper screen
- A strict JSON response contract
I use Gemini but it could be any model, really.
Gemini 2.5 Flash returns an object containing:
{
"title": "A short localized title",
"description": "Why the subject is relevant today",
"weekday_name": "Localized weekday",
"month_name": "Localized month",
"image_prompt": "A detailed English prompt for the image model"
}
Example prompt: Select a historical significant event for todays date
Example response: Use case: historical-scene
Asset type: M5Stack PaperColor daily display source image
Primary request: Depict the historically significant event of August 3, 1492: Christopher Columbus's first expedition departing Palos de la Frontera, Spain.
Scene/backdrop: dawn harbor with the three late-15th-century ships Santa María, Pinta, and Niña setting sail; low quay, mooring ropes, calm stylized sea, distant shoreline.
Style/medium: historically informed vintage travel-poster illustration with bold ink outlines and print-like texture.
Composition/framing: upright 3:2 landscape, 600x400 composition, the flagship centered and the two smaller caravels clearly distinct.
Color palette: strictly black, white, red, yellow, green, and blue; large flat areas, controlled contrast, minimal dithering.
Constraints: detailed yet legible on an e-paper display, no tiny rigging clutter.
Avoid: readable text, dates, captions, logos, watermark, QR code, gradients.
Step 3 - Generate the base artworkThe detailed image_prompt is sent to Gemini 3.1 Flash Image. The request specifies the active aspect ratio and asks for a small 512-class image rather than a huge source file. More resolution would only consume RAM, flash and network time before being reduced to a 400×600 panel.
The final instructions emphasize:
- Large, readable silhouettes
- A single obvious subject
- Flat areas of color
- Controlled detail and minimal texture
- No captions, date, QR code or interface elements
- Only black, white, red, yellow, green and blue
The image response is streamed to flash instead of being held entirely in memory. The program stores the server-sent-event response, extracts the inline Base64 image incrementally and caps the image and response sizes.
Step 4 - Fit the image to the screenThe device uses a “contain” fit. It scales the source until the complete image fits inside the target canvas and fills any unused area with white. It does not crop the sides just to make the screen full.
Step 5 - Reduce everything to six physical colorsThe M5Stack PaperColor uses an E Ink Spectra 6 panel. Its useful physical palette is:
- Black
- White
- Red
- Yellow
- Green
- Blue
That is not “a normal RGB screen with slightly muted colors.” There is no orange, brown, gray, skin tone, navy or pale blue pixel. Every output pixel must eventually become one of those six colors.
For non-JPEG results, the application converts the source to RGB, fits it to the target canvas and quantizes it against the six-color palette using Floyd–Steinberg dithering. Dithering creates the perception of intermediate colors by arranging available colors in small patterns. A gray cloud may become a controlled mixture of black and white pixels; a brown wooden object may become a pattern of red, yellow and black.
This introduces some useful design rules:
- Contrast beats subtlety. A bold black bird against a white or yellow sky survives. Six nearly identical blue-gray clouds do not.
- Shape beats texture. Large silhouettes remain recognizable after reduction; fine fur, grass and smoke often collapse into noise.
- Flat colors are valuable. Gradients become dither patterns, so important edges should not depend on a gentle tonal transition.
- Keep text out of the artwork. The device draws title and date itself using deterministic bitmap fonts.
- Generate for the final aspect ratio. Resizing is acceptable; re-composing a square picture into a landscape calendar is much harder.
JPEG image responses can be kept as the source and contain-fitted by the display widget. PNG and other raster responses use the explicit conversion path before display.
Step 6 - Add deterministic title and date overlaysThe title and date are not burned into the generated artwork. The PaperColor code creates small bitmap overlays and composes them with the image on a single canvas before committing the frame to the e-paper panel.
Keeping the overlays separate gives three advantages:
- Localized weekday and month names are always spelled correctly.
- The date remains legible regardless of what the image model created.
- Changing display orientation does not require regenerating the artwork.
A secondary description view can show a longer explanation and QR code. Waking the device manually can keep its small local configuration website available for five minutes; the normal scheduled wake leaves the web server off and returns to sleep as soon as the display is updated.
Step 7 - Refresh once and sleepFull-color e-paper is not fast. According to M5Stack, a PaperColor refresh typically takes roughly 10–20 seconds depending on the image. That delay is acceptable for a calendar because the content changes once per day, not sixty times per second.
After rendering, the program calculates the remaining seconds until the next local day, records its intended wake time and enters deep sleep. If generation fails, it uses a shorter retry sleep rather than waiting until tomorrow.
The panel retains the picture without continuously refreshing it, which is precisely why this project works as an always-visible object instead of a permanently glowing tablet.
The prompt libraryThe device ships with six themes. The active theme can be changed from its small configuration page (a QR code is shown by tappin any button. It will take you to a small webpage where prompts and be selected and users can add their ow),
There is nothing special or fixed about this list. The user can design a completely new prompt around an interest, an art style, a local event or practically any daily theme. As long as the prompt expander returns the same structured fields, the rest of the display pipeline remains unchanged.
For each example below I show the short instruction stored on the device, the detailed prompt produced for the image generator, the original generated image and the final six-color PaperColor version. The title is also saved with the result and rendered separately by the device.
Original Smithsonian portrait image (source)
Smithsonian portrait converted for PaperColor (six-color PaperColor result)
2. Historical eventOriginal departure from Palos image (source)
Departure from Palos converted for PaperColor (six-color PaperColor result)
3. Military historyOriginal 1914 mobilisation image (source)
1914 mobilisation converted for PaperColor (six-color PaperColor result)
4. Danish birdOriginal Caspian tern image (source)
Caspian tern converted for PaperColor (six-color PaperColor result)
5. Corporate motivationOriginal corporate embroidery image (source)
Corporate embroidery converted for PaperColor (six-color PaperColor result)
6. Invention blueprintOriginal Bell telephone blueprint image (source)
Bell telephone blueprint converted for PaperColor (six-color PaperColor result)
Roman Empire in the style of mosaicNow there is no longer any excuse not think of the roman empire
Roman Empire mosaic result
Building the project1. Prepare the PaperColorInstall the current UiFlow2 firmware for the M5Stack PaperColor and verify that the device appears as a USB serial device. The board identifies as an M5Stack PaperColor UiFlow2 device over USB CDC.
The application lives under /flash. The important files include:
/flash/main.py/flash/settings.json/flash/PostPrompt.txt/flash/imageproc_lite.py/flash/spleen_6x12.py/flash/data/
2. Configure Wi-Fi and GeminiUse the device configuration page to enter Wi-Fi, local time zone, orientation, language, active prompt and Gemini key. After that, the daily run is automatic.
Security note: Never publish a real Gemini API key in main.py, a Hackster code attachment or a screenshot. Use a placeholder in public examples, restrict the key where possible and rotate any key that has previously been stored in a shared file.
3. First runOn the first successful run, the device should:
- Initialize the PaperColor display.
- Load settings and connect to Wi-Fi.
- Synchronize the clock.
- Build the date-aware prompt.
- Request structured metadata from the text model.
- Stream the generated image to flash.
- Prepare overlays and render one complete canvas.
- Save metadata and cache files under /flash/data.
- Enter deep sleep until the next day.
The RGB status LEDs are useful while debugging: different colors identify Wi-Fi, Gemini request, image processing, transition and error phases without repeatedly refreshing the e-paper screen.
What I learnedPrompt engineering is part of the display driverWith a six-color panel, “make a nice image” is not enough. The prompt must understand the physical destination. Bold shapes and a controlled palette reduce both visual noise and the amount of corrective processing required later.
E-paper rewards doing lessThe best interface is one complete render followed by sleep. Progress bars, animations and frequent status updates are useful on LCDs but expensive and visually unpleasant on full-color e-paper.
Limitations- A complete refresh takes seconds rather than milliseconds.
- Dithered colors can look very different from the RGB source image.
- Image generation requires Wi-Fi and consumes API quota.
- Generated historical details should be treated as illustrative, not as a scholarly source.
- Very dense scenes, delicate gradients and small faces are difficult on six-color e-paper.
- A failed network or model response needs careful retry behavior to avoid draining the battery.
- Rotate themes automatically rather than using one active prompt.
- Add weather-aware or location-aware prompt context.
- Save favorite images to microSD as a permanent gallery.
- Use the temperature and humidity sensor as part of the daily artwork prompt.
- Add a “surprise me” hardware-button mode without changing the once-a-day schedule.
- M5Stack PaperColor documentation
- M5Stack PaperColor display documentation
- Gemini image-generation documentation
- Gemini Generate Content API
Project, device software, photographs and image experiments by Esben B.









Comments