Atomic is a lightweight MicroPython engine for fast 2D graphics on the Raspberry Pi Pico 2.
It uses MicroPython Viper where possible, and Native where not, for optimisation.
from atomic import graphics
High-performance Viper-optimised functions for 2D graphics.
Write a 16-bit RGB565 pixel into a linear framebuffer at (x, y).
The buffer must be a bytearray or memoryview of RGB565 pixels.
SetPixel(buf, x, y, color, width)
buf: framebuffer (bytearray or memoryview)x,y: pixel coordinatescolor: 16-bit RGB565 colourwidth: buffer width in pixels
Example
graphics.SetPixel(buf, 10, 12, 0xF800, 240)
Copy a 16×16 RGB565 tile into a 32×32 destination buffer at (dstX, dstY).
The destination buffer must be 32×32 RGB565 pixels (2048 bytes).
BlitTileToBuffer(tile, buf, dstX, dstY)
tile: 512-byte RGB565 tile (16×16)buf: destination buffer (2048-byte 32×32 area)dstX,dstY: offset in the buffer (usually 0 or 16)
Example
graphics.BlitTileToBuffer(tile, buffer, 0, 0)
Blend two 16-bit RGB565 colours using an 8-bit weight (0–255).
A higher weight yields more of the second colour.
BlendRGB565(colour1, colour2, weight)
colour1: RGB565 colour Acolour2: RGB565 colour Bweight: blend weight (0 = all A, 255 = all B)
Example
graphics.BlendRGB565(0xF800, 0x07E0, 128)
Blit a variable-sized RGB565 sprite with a transparent colour onto a linear framebuffer.
Useful for drawing sprites without overwriting background tiles.
BlitTransparentSprite(tile, buf, screenWidth, x, y, tileWidth, tileHeight, transparent)
tile: sprite data (tileWidth × tileHeight × 2 bytes)buf: framebuffer (e.g. screen or off-screen buffer)screenWidth: width of the framebuffer in pixelsx,y: target position to draw the spritetileWidth,tileHeight: dimensions of the spritetransparent: RGB565 colour to treat as transparent
Example
graphics.BlitTransparentSprite(sprite, framebuffer, 240, 64, 32, 16, 16, WHITE)
Convert 8-bit per channel RGB values to a single 16-bit RGB565 value.
RGBto565(r, g, b)
r,g,b: 8-bit (0–255) colour channels- Returns: 16-bit RGB565 colour
Example
graphics.RGBto565(255, 0, 0)
Convert a hex string (e.g. "#FFAA33") into a 16-bit RGB565 colour.
Must be a 6-character hex code with or without a leading #.
HEXto565(hexStr)
hexStr: a string like"#FFAABB"or"FFAABB"- Returns: 16-bit RGB565 colour
Example
graphics.HEXto565("#00FF00")
from atomic import tileutils
Efficient tools for working with tilemaps.
Convert screen (pixel) coordinates into tilemap (grid) coordinates.
GetTileCoords(x, y, tileSize)
x,y: screen position in pixelstileSize: tile width/height (e.g. 16)- Returns:
(tileX, tileY)tuple
Example
tileX, tileY = tileutils.GetTileCoords(playerX, playerY, 16)
Same as GetTileCoords, but returns a packed 32-bit int instead of a tuple.
Mainly for internal use or performance-critical logic.
GetTileCoordsPacked(x, y, tileSize)
- Returns: packed int
(tileY << 16) | tileX
Return the bounding tile coordinates that cover a rectangular region of a given radius.
This is useful for visibility, collision, or range checks.
GetCoveredTileCoords(x, y, radius)
x,y: centre in pixelsradius: radius in pixels- Returns:
(top, bottom, left, right)tile indices
Example
top, bottom, left, right = tileutils.GetCoveredTileCoords(playerX, playerY, 24)
Same as GetCoveredTileCoords, but returns a packed 32-bit int.
Mainly for internal use or performance-critical logic.
GetCoveredTileCoordsPacked(x, y, radius)
- Returns: packed int
(top << 24) | (bottom << 16) | (left << 8) | right
from atomic import utilities
Efficient helper functions for core tasks.
Check if a button is currently being pressed (i.e. its GPIO pin is low).
This is useful for reading physical button states with internal pull-ups.
Pressed(pin)
pin: amachine.Pinobject (configured asPin.INwithPin.PULL_UP)- Returns:
Trueif the button is pressed,Falseotherwise
Example
iA = utilities.Pressed(iA)
Render aligned text to the screen using a bitmap font.
Supports optional justification to centre or align text.
DrawText(display, font, msg, x, y, fg, bg, jx=0, jy=0)
display: the target display object (e.g.display)font: a bitmap font object (must have.WIDTHand.HEIGHT)msg: string to renderx,y: position in pixelsfg,bg: foreground and background colours (RGB565)jx,jy: justification (0 = left/top, 0.5 = centre, 1 = right/bottom)
Example
utilities.DrawText(display, font8, "Hello!", 120, 100, WHITE, BLACK, 0.5, 0.5)
Automatically break a long string into multiple lines that fit a fixed-width text box – perfect for dialogue windows, menus, and intro screens.
WrapText(msg, maxChars)
msg: full text (may contain\n, which forces a new paragraph)maxChars: maximum characters per rendered line- Returns:
list[str]– each element is a line no longer thanmaxChars
The function keeps whole words together; if a single word is longer
than maxChars it is placed on its own line.
Existing \n characters are respected as hard paragraph breaks.
Example
lines = utilities.WrapText(
"Electricity now costs $1 per watt-second — a 1,400,000,000% increase!", 26
)
y = 20
for line in lines:
utilities.DrawText(display, font8, line, 120, y, BLACK, WHITE, 0.5, 0.5)
y += font8.HEIGHTGenerate a random float using a normal distribution with optional clamping.
BellCurve(mean, sigma=1.0, lo=None, hi=None)
mean: Centre of the distributionsigma: Spread (standard deviation)lo,hi: Optional min/max bounds- Returns: float value sampled from a bell curve
Example
damage = utilities.BellCurve(10, 2, 5, 15)
Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 (CC BY-NC-ND 4.0)
- Free for personal and educational use
- No commercial use
- No modification or redistribution without permission
(c) 2025 Henry Gurney