Skip to content

MLP210: fixed-count loop issues many short sequential plays

qual reports MLP210 when a self.play(...) statement is a direct child of a for loop whose literal trip count (an int-literal range(...) or a display literal) proves at least 8 iterations: every iteration pays the full play startup — animation compile, hash, begin starting copies, cleanup — before a single frame renders.

  • Default severity: info
  • Minimum confidence: medium (the loop itself may be conditionally reached; the per-iteration count is proven)
  • Implementation phase: 3
  • Fix: none (grouping into one play changes pacing choices)

Threshold (documented constant in rules/performance/loop_plays.rs):

  • MLP210_LOOP_PLAYS_GATE = N_plays >= 8.

Gates that keep the count honest: the play must be a direct statement of the loop body (a play behind an if does not run once per iteration), the body must contain no break / continue / return / raise, and the trip count must be a literal. Per DESIGN 7.3, the per-play partial-stream boundary cost is not included — no OutputState fact proves a per-play output stream exists (a continuous writer has none) — so the overhead is described qualitatively.

Wrong

class Demo(Scene):
    def construct(self):
        square = Square()
        for _ in range(10):
            self.play(FadeIn(square), run_time=0.5)
class Demo(Scene):
    def construct(self):
        dots = VGroup(*[Dot() for _ in range(10)])
        self.play(LaggedStart(*[FadeIn(dot) for dot in dots]), run_time=5)

AnimationGroup runs the animations together; LaggedStart reproduces the one-after-another stagger — either way the play startup is paid once.