\documentclass[border=20pt]{standalone}
\usepackage{svg-animate}

%% ── Static housing (always visible, drawn outside animate) ───────────────────

\newcommand{\housing}{%
  \fill[black!80, rounded corners=6pt] (-1.2,-3.6) rectangle (1.2,3.6);
  \fill[black!50, rounded corners=4pt] (-1.0,-3.4) rectangle (1.0,3.4);
  \foreach \y in {-2.2, 0, 2.2}
    { \fill[black!60] (0,\y) circle[radius=0.85]; }
}

%% ── Per-step content macros ──────────────────────────────────────────────────
%%
%% NOTE: \animstep must appear LITERALLY in each animate body — the +b
%% collector does not expand macros before splitting on \animstep.
%% Only the step *content* is factored into these macros.

\newcommand{\stepOne}{%
  \reveal{%
    \fill[green!65!black]  (0,-2.2) circle[radius=0.75];
    \node[right, font=\sffamily, black!70] at (1.3,-2.2) {fr. 1};
  }%
}

\newcommand{\stepTwo}{%
  \reveal{%
    \fill[orange!90!black] (0, 0.0) circle[radius=0.75];
  }%
  %% Label shared with step 4: use step= key so it appears in both frames
  \reveal[step={2,4}]{%
    \node[right, font=\sffamily, black!70] at (1.3, 0.0) {fr. 2+4};
  }%
}

\newcommand{\stepThree}{%
  \reveal{%
    \fill[red!85!black]    (0, 2.2) circle[radius=0.75];
  }%
  %% Label shared with step 4: use step= key so it appears in both frames
  \reveal[step={3,4}]{%
    \node[right, font=\sffamily, black!70] at (1.3, 2.2) {fr. 3+4};
  }%
  %% Blinking marker: demonstrates blink= key.
  %% The static column is immune because it uses \begin{animate}[static].
  \reveal[blink=0.3,]{%
    \node[fill=yellow!80, draw=orange!80!black, font=\sffamily,
          inner sep=2pt, rounded corners=2pt] at (0,-4.0) {fr. 3 — blink};
  }%
  %% step={1-3}: visible in steps 1 to 3 — demonstrates range syntax
  \reveal[step={1-3}]{%
    \node[font=\sffamily, black!50] at (0,-5.6) {step=\{1-3\}};
  }%
  %% Demonstrates blink off opacity: during step 3 the element blinks between
  %% blink on opacity=1 (visible) and blink off opacity=0.25 (dimmed),
  %% while inactive opacity=0 keeps it hidden between steps.
  \reveal[blink=0.3, inactive opacity=0, blink off opacity=0.25]{%
    \node[fill=yellow!80, draw=orange!80!black, font=\sffamily,
          inner sep=2pt, rounded corners=2pt] at (0,-6.2) {fr. 3 — dimmed blink};
  }%

}

\newcommand{\stepFour}{%
  \reveal{%
    \fill[red!85!black]    (0, 2.2) circle[radius=0.75];
  }%
  \reveal{%
    \fill[orange!90!black] (0, 0.0) circle[radius=0.75];
  }%
  %% step={1,2,3,4}: visible in every frame — demonstrates the step= key
  \reveal[step={1,2,3,4}]{%
    \node[font=\sffamily, black!50] at (0,-4.8) {step=\{1,2,3,4\}};
  }%
}

\begin{document}
\begin{tabular}{@{}c@{\qquad}c@{\qquad}c@{}}
  \small\texttt{loop=true}  &
  \small\texttt{loop=false} &
  \small static (all steps) \\[6pt]

  %% ── Column 1 : loops forever ─────────────────────────────────────────────
  \begin{tikzpicture}
    \housing
    \begin{animate}[inactive opacity=0.08, duration=1.5]
      \stepOne
      \animstep
      \stepTwo
      \animstep[duration=0.5]
      \stepThree
      \animstep
      \stepFour
    \end{animate}
  \end{tikzpicture}

  &

  %% ── Column 2 : plays once, rests on \noanimate frame ─────────────────────
  \begin{tikzpicture}
    \housing
    \begin{animate}[inactive opacity=0.08, duration=1.5, loop=false]
      %% Resting state shown after the animation ends (SMIL fill=remove).
      \noanimate{%
        \foreach \y in {-2.2, 0, 2.2}
          { \fill[black!35] (0,\y) circle[radius=0.75]; }
        \node[font=\sffamily, black!50] at (0,-4.2) {end state};
      }%
      \stepOne
      \animstep
      \stepTwo
      \animstep[duration=0.5]
      \stepThree
      \animstep
      \stepFour
    \end{animate}
  \end{tikzpicture}

  &

  %% ── Column 3 : static — all steps at opacity 1 simultaneously ────────────
  \begin{tikzpicture}
    \housing
    \begin{animate}[static]
      \stepOne
      \animstep
      \stepTwo
      \animstep[duration=0.5]
      \stepThree
      \animstep
      \stepFour
    \end{animate}
  \end{tikzpicture}

\end{tabular}
\end{document}
