# Understanding the code

There is a lot going on to bring these images to life on the LED matrix, so let's break things down a little bit.

Begin by importing the necessary modules and ensuring that we can create our display:

```import math
import time
import random

import adafruit_imageload.bmp
import board
import displayio
import framebufferio
import rgbmatrix
import ulab

displayio.release_displays()```

RGBMatrix doesn't have a brightness control, so the `Reshader` class uses ulab to quickly apply a brightness value from 0.0 to 1.0 to an original palette, updating the palette accordingly. This code works pretty quickly, especially when you make sure your images have a pallette of just a few dozen colors.

```class Reshader:
'''reshader fades the image to mimic brightness control'''
def __init__(self, palette):
self.palette = palette
ulab_palette = ulab.zeros((len(palette), 3))
for i in range(len(palette)):
rgb = int(palette[i])
ulab_palette[i, 2] = rgb & 0xff
ulab_palette[i, 1] = (rgb >> 8) & 0xff
ulab_palette[i, 0] = rgb >> 16
self.ulab_palette = ulab_palette

def reshade(self, brightness):
'''reshader'''
palette = self.palette
shaded = ulab.array(self.ulab_palette * brightness, dtype=ulab.uint8)
for i in range(len(palette)):
palette[i] = tuple(shaded[i])```

`do_crawl_down` makes an image move from the top of the screen to the bottom. Here are what all the parameters mean:  `image_file` is the filename of an image. Images up to 64x64 pixels work best. `speed` is the down speed in pixels per second. `weave` gives the amount the image weaves left and right, 0 for no weave. `weave_speed` controls how quickly the weave goes. `pulse` controls how much the brightness changes, values from 0 (no brightness change) to 0.5 work well.

By working with time in integer nanoseconds, we avoid rounding errors that occur when working with time in floating-point seconds. It makes the code a bit more complicated, but avoids the animation becoming "jerky" after running continuously for more than a day or so.

```def do_crawl_down(image_file, *,
speed=12, weave=4, pulse=.5,
weave_speed=1/6, pulse_speed=1/7):
'''function to scroll the image'''
the_bitmap, the_palette = adafruit_imageload.load(
image_file,
bitmap=displayio.Bitmap,
palette=displayio.Palette)

shader = Reshader(the_palette)

group = displayio.Group()
tile_grid = displayio.TileGrid(bitmap=the_bitmap, pixel_shader=the_palette)
group.append(tile_grid)
display.show(group)

start_time = time.monotonic_ns()
start_y = display.height   # High enough to be "off the top"
end_y = -the_bitmap.height     # Low enough to be "off the bottom"

# Mix up how the bobs and brightness change on each run
r1 = random.random() * math.pi
r2 = random.random() * math.pi

y = start_y
while y > end_y:
now = time.monotonic_ns()
y = start_y - speed * ((now - start_time) / 1e9)
group.y = round(y)

# wave from side to side
group.x = round(weave * math.cos(y * weave_speed + r1))

# Change the brightness
if pulse > 0:
shader.reshade((1 - pulse) + pulse * math.sin(y * pulse_speed + r2))

display.refresh(minimum_frames_per_second=0, target_frames_per_second=60)```

`do_pulse` omits the movement part of do_crawl_down, so it's basically a simplified version of `do_crawl_down`.

```def do_pulse(image_file, *, duration=4, pulse_speed=1/8, pulse=.5):
'''pulse animation'''
the_bitmap, the_palette = adafruit_imageload.load(
image_file,
bitmap=displayio.Bitmap,
palette=displayio.Palette)

shader = Reshader(the_palette)

group = displayio.Group()
tile_grid = displayio.TileGrid(bitmap=the_bitmap, pixel_shader=the_palette)
group.append(tile_grid)
group.x = (display.width - the_bitmap.width) // 2
group.y = (display.height - the_bitmap.height) // 2
display.show(group)

start_time = time.monotonic_ns()
end_time = start_time + int(duration * 1e9)

now_ns = time.monotonic_ns()
while now_ns < end_time:
now_ns = time.monotonic_ns()
current_time = (now_ns - start_time) / 1e9

shader.reshade((1 - pulse) - pulse
* math.cos(2*math.pi*current_time*pulse_speed)**2)

display.refresh(minimum_frames_per_second=0, target_frames_per_second=60)```

Create the display object. Check out our dedicated guide for rgbmatrix to find pinouts for other boards besides the Feather M4 Express!

```matrix = rgbmatrix.RGBMatrix(
width=64, height=32, bit_depth=5,
rgb_pins=[board.D6, board.D5, board.D9, board.D11, board.D10, board.D12],
addr_pins=[board.A5, board.A4, board.A3, board.A2],
clock_pin=board.D13, latch_pin=board.D0, output_enable_pin=board.D1)
display = framebufferio.FramebufferDisplay(matrix, auto_refresh=False)```

Choose randomly from 5 different effects. By using different images and different combinations of parameters, there's more variety. Why not try picking your own combinations, and see what you like best! Head to the next page for more information about customizing this with your own images.

```# Image playlist - set to run randomly. Set pulse from 0 to .5
while True:
show_next = random.randint(1, 5) #change to reflect how many images you add
if show_next == 1:
do_crawl_down("/ray.bmp")
elif show_next == 2:
do_crawl_down("/waves1.bmp", speed=7, weave=0, pulse=.35)
elif show_next == 3:
do_crawl_down("/waves2.bmp", speed=9, weave=0, pulse=.35)
elif show_next == 4:
do_pulse("/heart.bmp", duration=4, pulse=.45)
elif show_next == 5:
do_crawl_down("/dark.bmp")```

This guide was first published on Aug 12, 2020. It was last updated on Aug 12, 2020.

This page (Code Explanation) was last updated on Apr 04, 2021.

Text editor powered by tinymce.