Here are two classic startup screens re-implemented in CircuitPython. To use them click the 'Download Project Bundle' button, then unzip the bundle and copy the code.py and libraries to your devices CIRCUITPY drive.
GameBoy
The GameBoy startup is simple but iconic. The Nintendo logo slides in from the top of the screen and when it reaches it stopping position a pleasant pling sound effect reminiscent of collection coins in Mario or other games. The CircuitPython version loads the logo Bitmap with adafruit_imageload and places it over the top of a background made from a solid color Bitmap scaled to match the display size. The y position of the TileGrid with the logo in it is updated at set intervals to slide down from the top of the display. Once it reaches the stopping position the pling sound wave file is played using the TLV320 DAC output.
# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries
# SPDX-License-Identifier: MIT
import time
from audiocore import WaveFile
import audiobusio
import board
from displayio import Group, TileGrid, Bitmap, Palette
import supervisor
import adafruit_imageload
import adafruit_tlv320
from adafruit_fruitjam.peripherals import request_display_config
# how long between animation frames
ANIMATE_INTERVAL = 1 / 45
background_color = 0xE1F7CE
i2c = board.I2C()
dac = adafruit_tlv320.TLV320DAC3100(i2c)
dac.configure_clocks(sample_rate=44100, bit_depth=16)
# for headphone jack ouput
dac.headphone_output = True
dac.headphone_volume = -15 # dB
# for speaker JST output
# dac.speaker_output = True
# dac.speaker_volume = -15 # dB
wave_file = open("gameboy_startup/gameboy_pling.wav", "rb")
wave = WaveFile(wave_file)
audio = audiobusio.I2SOut(board.I2S_BCLK, board.I2S_WS, board.I2S_DIN)
# display setup
request_display_config(320, 240)
display = supervisor.runtime.display
# group to hold all visual elements
main_group = Group()
# Bitmap for background color
bg_bmp = Bitmap(display.width // 20, display.height // 20, 1)
bg_palette = Palette(1)
bg_palette[0] = background_color
bg_tg = TileGrid(bg_bmp, pixel_shader=bg_palette)
# group to scale the background bitmap up to display size
bg_group = Group(scale=20)
bg_group.append(bg_tg)
main_group.append(bg_group)
# Bitmap for logo
logo, palette = adafruit_imageload.load("gameboy_startup/gameboy_logo.bmp")
logo_tg = TileGrid(logo, pixel_shader=palette)
main_group.append(logo_tg)
# place it in the center horizontally and above the top of the display
logo_tg.x = display.width // 2 - logo_tg.tile_width // 2
logo_tg.y = -logo_tg.tile_height
# y pixel location to stop logo at
STOP_Y = display.height * 0.4 - logo_tg.tile_height // 2
display.root_group = main_group
time.sleep(1.5)
last_animate_time = time.monotonic()
played_audio = False
display.auto_refresh = False
while True:
now = time.monotonic()
# if it's time to animate and the logo isn't to the
# stopping position yet
if last_animate_time + ANIMATE_INTERVAL <= now and logo_tg.y < STOP_Y:
# update the timestamp
last_animate_time = now
# move the logo down by a pixel
logo_tg.y += 1
display.refresh()
# if the logo has reached the stop position
if logo_tg.y >= STOP_Y and not played_audio:
played_audio = True
# play the audio pling
audio.play(wave)
while audio.playing:
pass
Mac System 7.5
With the release of System 7.5, Apple added a progress bar to the startup screen, as well as some icons along the bottom stylized like jigsaw puzzle pieces. The CircuitPython implementation uses an OnDiskBitmap showing an image that contains the background and welcome message box. A progress bar and series of TileGrids containing icons are layered on top of the background. The progress bar is updated over time, and at specified intervals a new icon gets revealed by settings its hidden property to False.
# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries
# SPDX-License-Identifier: MIT
import time
from audiocore import WaveFile
import audiobusio
import board
import supervisor
from displayio import Group, TileGrid, OnDiskBitmap
import adafruit_tlv320
from adafruit_fruitjam.peripherals import request_display_config
from adafruit_progressbar.horizontalprogressbar import (
HorizontalFillDirection,
HorizontalProgressBar,
)
# DAC setup
i2c = board.I2C()
dac = adafruit_tlv320.TLV320DAC3100(i2c)
dac.configure_clocks(sample_rate=44100, bit_depth=16)
# for headphone jack ouput
dac.headphone_output = True
dac.headphone_volume = -15 # dB
# for speaker JST output
# dac.speaker_output = True
# dac.speaker_volume = -15 # dB
# Chime audio setup
wave_file = open("mac_startup/mac_chime.wav", "rb")
wave = WaveFile(wave_file)
audio = audiobusio.I2SOut(board.I2S_BCLK, board.I2S_WS, board.I2S_DIN)
# Display setup
request_display_config(640, 480)
display = supervisor.runtime.display
display.auto_refresh = False
# group to hold visual all elements
main_group = Group()
display.root_group = main_group
display.refresh()
# background image
bg_bmp = OnDiskBitmap("mac_startup/mac_startup_bg.bmp")
bg_tg = TileGrid(bg_bmp, pixel_shader=bg_bmp.pixel_shader)
main_group.append(bg_tg)
# Icons for bottom left
icons = []
for i in range(6):
odb = OnDiskBitmap("mac_startup/mac_startup_icon{0}.bmp".format(i))
tg = TileGrid(odb, pixel_shader=odb.pixel_shader)
icons.append(
{
"bmp": odb,
"tg": tg,
}
)
tg.x = 10 + ((33 + 8) * i)
tg.y = display.height - tg.tile_height - 10
tg.hidden = True
if i < 5:
odb.pixel_shader.make_transparent(0)
main_group.append(tg)
# progress bar in the welcome box
progress_bar = HorizontalProgressBar(
(147, 138),
(346, 7),
direction=HorizontalFillDirection.LEFT_TO_RIGHT,
min_value=0,
max_value=800,
fill_color=0xC7BEFD,
outline_color=0x000000,
bar_color=0x3F3F3F,
margin_size=0,
)
main_group.append(progress_bar)
# play the chime sound
audio.play(wave)
while audio.playing:
pass
# start drawing the visual elements
display.auto_refresh = True
time.sleep(1)
start_time = time.monotonic()
while True:
elapsed = time.monotonic() - start_time
# if we haven't reached the end yet
if elapsed * 100 <= 800:
# update the progress bar
progress_bar.value = elapsed * 100
else: # reached the end animation
# set progress bar to max value
progress_bar.value = 800
# loop over all icons
for index, icon in enumerate(icons):
# if it's time for the current icon to show, and it's still hidden
if (elapsed - 1) > index and icon["tg"].hidden:
# make the current icon visible
icon["tg"].hidden = False
Page last edited June 13, 2025
Text editor powered by tinymce.