Created
May 21, 2026 13:07
-
-
Save grenkoca/06b76c2a235a00f0856a86639d6e5768 to your computer and use it in GitHub Desktop.
Create in-line animations in Jupyter notebooks from matplotlib data
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import matplotlib.pyplot as plt | |
| from matplotlib.animation import FuncAnimation | |
| from IPython.display import display, HTML | |
| import base64 | |
| def gif_animator(interval=200, repeat=True, figsize=(5, 5), dpi=100): | |
| """ | |
| A generalized decorator that converts ANY plotting function into an | |
| in-line, perfectly-cropped Jupyter GIF animation. | |
| The decorated function MUST accept two arguments: (state_data, ax) | |
| and return a list of matplotlib artists to update, or nothing if | |
| ax.clear() is handled manually. | |
| Example usage: | |
| ``` | |
| @gif_animator(interval=150, figsize=(4, 4), dpi=100) | |
| def draw_matrix_frame(matrix_state, ax, cmap='viridis'): | |
| ax.imshow(matrix_state, cmap=cmap, interpolation='nearest') | |
| matrix_history = [s.hamming_matrix for s in results.snapshots] | |
| draw_matrix_frame(matrix_history, cmap='plasma') | |
| ``` | |
| """ | |
| def decorator(plot_func): | |
| def wrapper(states, *args, **kwargs): | |
| if not states: | |
| print("No state data provided to animate.") | |
| return None | |
| fig = plt.figure(figsize=figsize, dpi=dpi, frameon=False) | |
| ax = fig.add_axes([0, 0, 1, 1]) # 100% canvas coverage | |
| def update(frame_idx): | |
| ax.clear() # Wipe the previous frame's geometry | |
| ax.axis('off') # Keep axes hidden after clearing | |
| # Call your custom plotting script for this specific frame state | |
| plot_func(states[frame_idx], ax, *args, **kwargs) | |
| return [] | |
| # 3. Compile the animation loop | |
| anim = FuncAnimation( | |
| fig, | |
| update, | |
| frames=len(states), | |
| interval=interval, | |
| repeat=repeat | |
| ) | |
| plt.close(fig) # Prevent ghost duplicate display in Jupyter | |
| # 4. Save cleanly using the Pillow engine | |
| gif_path = '_temp_generalized_anim.gif' | |
| anim.save( | |
| gif_path, | |
| writer='pillow', | |
| fps=1000/interval, | |
| savefig_kwargs={ | |
| 'pad_inches': 0, | |
| 'transparent': True | |
| } | |
| ) | |
| # 5. Inject CSS to completely bypass Jupyter cell padding boundaries | |
| with open(gif_path, 'rb') as f: | |
| encoded_gif = base64.b64encode(f.read()).decode('utf-8') | |
| # Map inches directly back to browser pixels for rendering layout | |
| pixel_width = int(figsize[0] * dpi) | |
| pixel_height = int(figsize[1] * dpi) | |
| html_str = f''' | |
| <div style="display: inline-block; padding: 0; margin: 0; line-height: 0; vertical-align: top;"> | |
| <img src="data:image/gif;base64,{encoded_gif}" | |
| style="display: block; padding: 0; margin: 0; max-width: none; width: {pixel_width}px; height: {pixel_height}px;" /> | |
| </div> | |
| ''' | |
| display(HTML(html_str)) | |
| return states | |
| return wrapper | |
| return decorator |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment