The fast_fade example in 'Single File Examples' has a bug in it. It SHOULD show a black "ONE" on a white background fading out as a white "TWO" on a black background fades in. However if you run the example the "ONE" isn't actually shown at all, only the "TWO" fades.
The bug is subtle. In the playdate.update() function the focus is locked to the frontImage mask which is then cleared to black (making frontImage transparent). Then patternWithOpacity() is called to get a pattern which is then tiled into the mask and the focus unlocked. However patternWithOpacity() itself calls lock/unlockFocus() so when that returns the focus is actually unlocked. The pattern, instead of being written to the mask, is just written to the screen directly and then frontImage is drawn on top, however that's entirely transparent. The effect looks very similar, however 'ONE' is missing and what's actually happening is nothing to do with masks at all.
One fix is to push/pop the graphics context inside patternWithOpacity() which saves the graphics focus. Or to call patternWithOpacity before locking focus.
This bug does highlight a potentially bad semantic in lock/unlockFocus(). Any routine could call it to do drawing and not everyone will think about pushing/popping the graphics context, which may also be an expensive operation. I think either lock/UnlockFocus() should stack calls so that it really works more like push/popFocus() or calling lockFocus() when already locked, or unlockFocus() when unlocked should assert, perhaps with a message suggesting nested lock/unlockFocus calls need the graphics context to be pushed. Personally I'd prefer the stacked version where lockFocus(A)->lockFocus(B)->unlockFocus()->unlockFocus() switched from screen -> A -> B -> A -> screen which I think is what most people would expect to happen.