Draws Using Previous Frame State
← All Butter documentationIn general, Butter components will have a better user experience if they draw directly vased on time and fields, rather than on the state of variables that get incrementally updated every draw. If a component relies on incremental state, then it will be a little less responsive when users update its fields.
When editing your component, in the right sidebar, there is a toggle at the top labelled Draws Using Previous Frame State:
-
If a sketch does not draw using the previous frame state, it means that it fully redraws the canvas every frame using only field values and no data recreated from the previous frame.
This means that you can seek anywhere on the timeline and the sketch can immediately draw the frame.
-
If a sketch does draw using the previous frame state, then when you seek ahead, it has to quickly go through all the in between frames on its way there to make sure it looks correct at the end.
There are a few ways in which your sketch might be using previous frame state:
-
You don't fully clear the canvas each frame, so visuals build up over time:

-
You incrementally update variables each frame (e.g. by calling
x++indraw, or running a physics simulation):
Butter will try to automatically detect when your sketch needs Draws Using Previous Frame State toggled on. You can always manually toggle it on or off.
Best Practices
Even though when seeking, Butter will quickly go through all intermediate frames, that can't always be done in playback. In playback, frames will be dropped in order to keep in sync with audio. This means that playback in studio may slightly drift compared to exports, which always go one frame at a time. While pausing or seeking will always take its time to show you the exact state of the sketch, playback in studio cannot afford that luxury. To prevent studio playback from drifting from exports, ideally, write code that does not incrementally update state. But if that is necessary for a physica simulation, make sure to take deltaTime, the time between frames, into account rather than using a fixed time step:
// Don't do a fixed time step
position = position.copy().add(velocity.copy().mult(1 / 30))
// Instead, use deltaTime:
position = position.copy().add(velocity.copy().mult(deltaTime / 1000))