Skip to content

Introduce seed in random_color method to produce colors deterministically #4265

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
88 changes: 87 additions & 1 deletion manim/utils/color/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1508,9 +1508,94 @@
ManimColor
A random :class:`ManimColor`.
"""
return RandomColorGenerator().next()
Copy link
Author

@ishu9bansal ishu9bansal Jun 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@behackl
While thinking about the warning that you mentioned.
Do you think it would make sense to use a singleton object here instead of initializing a new one each time?

In a scenario where random_color is called frequently across a project, repeatedly creating and discarding instances could be inefficient (though I'm not entirely sure how Python handles cleanup in this case).

Would it be better to define a static version of the method, allowing us to directly call next() on the class rather than an instance?

Open to your thoughts or suggestions. Happy to include this as part of the current PR or address it as a follow-up improvement.



class RandomColorGenerator:
"""
A generator for producing random colors from a given list of Manim colors,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"""
A generator for producing random colors from a given list of Manim colors,
"""A generator for producing random colors from a given list of Manim colors,

optionally in a reproducible sequence using a seed value.

When initialized with a specific seed, this class produces a deterministic
sequence of `ManimColor` instances. If no seed is provided, the selection is
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
sequence of `ManimColor` instances. If no seed is provided, the selection is
sequence of :class:`.ManimColor` instances. If no seed is provided, the selection is

non-deterministic using Python’s global random state.

Parameters
----------
seed : int | None, optional
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type annoations are taken from the actual type hints, no need to add them twice

Suggested change
seed : int | None, optional
seed

A seed value to initialize the internal random number generator.
If None (default), colors are chosen using the global random state.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
If None (default), colors are chosen using the global random state.
If ``None`` (the default), colors are chosen using the global random state.


sample_colors : list[ManimColor], optional
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
sample_colors : list[ManimColor], optional
sample_colors

A custom list of Manim colors to sample from. Defaults to the full Manim color palette.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
A custom list of Manim colors to sample from. Defaults to the full Manim color palette.
A custom list of Manim colors to sample from. Defaults to the full Manim
color palette.


Examples
--------
Without a seed (non-deterministic):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The actual formatting for the example blocks need to be slightly adapted to make the documentation render correctly. I'll push a commit to take care of it. 👍

>>> from manim import RandomColorGenerator, ManimColor, RED, GREEN, BLUE
>>> rnd = RandomColorGenerator()
>>> isinstance(rnd.next(), ManimColor)
True

With a seed (deterministic sequence):
>>> rnd = RandomColorGenerator(42)
>>> rnd.next()
ManimColor('#ECE7E2')
>>> rnd.next()
ManimColor('#BBBBBB')
>>> rnd.next()
ManimColor('#BBBBBB')

Re-initializing with the same seed gives the same sequence:
>>> rnd2 = RandomColorGenerator(42)
>>> rnd2.next()
ManimColor('#ECE7E2')
>>> rnd2.next()
ManimColor('#BBBBBB')
>>> rnd2.next()
ManimColor('#BBBBBB')

Using a custom color list:
>>> custom_palette = [RED, GREEN, BLUE]
>>> rnd_custom = RandomColorGenerator(1, sample_colors=custom_palette)
>>> rnd_custom.next() in custom_palette
True
>>> rnd_custom.next() in custom_palette
True

Without a seed and custom palette (non-deterministic):
>>> rnd_nodet = RandomColorGenerator(sample_colors=[RED])
>>> rnd_nodet.next()
ManimColor('#FC6255')
"""

import manim.utils.color.manim_colors as manim_colors

return random.choice(manim_colors._all_manim_colors)
def __init__(
self,
seed: int | None = None,
sample_colors: list[ManimColor] = manim_colors._all_manim_colors,
) -> None:
self.choice = random.choice if seed is None else random.Random(seed).choice
self.colors = sample_colors

def next(self) -> ManimColor:
"""
Returns the next color from the configured color list.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"""
Returns the next color from the configured color list.
"""Return the next color from the configured color list.


Returns
-------
ManimColor
A randomly selected color from the specified color list.

Examples
--------
>>> from manim import RandomColorGenerator, RED
>>> rnd = RandomColorGenerator(sample_colors=[RED])
>>> rnd.next()
ManimColor('#FC6255')
"""
return self.choice(self.colors)


def get_shaded_rgb(
Expand Down Expand Up @@ -1566,6 +1651,7 @@
"average_color",
"random_bright_color",
"random_color",
"RandomColorGenerator",
"get_shaded_rgb",
"HSV",
"RGBA",
Expand Down
Loading