TL;DR Are there any design patterns or strategies for updating lots of LED animations? Or, perhaps, does grid have a “FPS” limit?
Hey all. I’ve got a project I’m working on that involves lots of moving LEDs. There are “signals” that move about the grid and “cells” that are stationary, but the user can place them everywhere. There are submenus, multi-finger gestures, etc. This is an M4L device and, to my horror, when I turned on the Live playback everything started lagging horrendously. After I hit stop it took many seconds for the animations and redraws to catch up. Obviously not acceptable. I haven’t done a deep dive into finding the bottleneck but my first hypothesis is that I shouldn’t be clearing the whole grid and redrawing every LED for each beat… but the idea of intelligently tracking every LED state and toggling them atomically makes me a little queazy. What is strange to me is that even with a simple arrangement of cells and signals it lags. Also the flickering looks crappy.
Here’s a video, some screenshots of the patch, and some of the JS running it. Essentially I’ve got the state of all the objects stored in JSON and I push that out to [dict]s on each beat. Then I clear the field (see clearField) below and redraw everything. I can’t use stuff like clear messages or /monome/grid/led/all 0 because column zero is a menu. Thank you.
Taking advantage of things like /grid/led/map, /grid/led/row, and /grid/led/col would be a good first step. Those work much faster than setting LEDs individually.
If I find myself needing to redraw all or most of the grid often, I’ll store and manipulate the grid state in a buffer, and then draw the whole buffer all at once (well, row by row). No clearing needed with /grid/led/all 0 this way, so this should get rid of the flickering.
Perhaps you ‘shouldn’t’ use those messages for some reason, but it could well be much easier to redraw the whole display (including menu) every frame.
The best reference for learning drawing styles is the Grid Studies. I borrowed the below from there.
If you want to draw line-by-line, you can use eg: /monome/grid/led/level/row 0 6 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 to draw row 6 (zero-based), with the lights fading up in level left to right. The first 0 just says ‘start at the left edge’, but you can offset by 8 if you want to split the grid horizontally.
Personally I’d just use the /grid/led/level/map command which requires you send the commands in 8x8 grids. The full spec is here. Can’t think of any grid examples, but a lot of the Arc apps use this approach so that might be a good place to search.
Something I’ve found to help as well, and that is if you have a way to quickly reference current LED/column/row state of your grid, is to create a wrapper function around your lower-level LED/column/row on and off functions that checks whether an LED/column/row actually needs to send a state change to the monome grid–if an LED/column/row is already what it needs to be then no-op and keep moving.
Really simple trick, but it dramatically cut down on the OSC/serial/USB traffic for me with a couple simple 2-D array checks.
the single LED and row/col commands are bandwidth-optimized only if you’re doing very simple operations— and in the early days we had tons of patches that did simple stuff where this was enough.
i’ve long since abandoned this approach. on norns and the eurorack modules the only serial packets sent are full 8x8 “map” arrays, if the area is dirty. granted, there’s no OSC flying around.
my suggestion: for complex applications (even in max, use js instead) just redraw the entire 8x8 grid every refresh. you can get very fast refresh rates this way, and the CPU will hardly suffer.
you can have separate functions for drawing a nav bar, different views, etc, and they all can just get called by a central grid redraw. this redraw can even be on a timer so you don’t need to worry about dirty states (less efficient, but nothing will explode).
Hi, I just wanted to thank you for this. As I started building more complex grid patches, I was facing the same led refreshing issues and since the consensus is to handle map messages via js inside max msp the end of the road seemed far away (I have no experience with js). Your conversion patch has made it a breeze to work with coll objects and map messages (and most of all provided great knowledge). Thanks a lot for sharing!