Profile pictures that are computed, not drawn: the Hopf fibration, a Calabi–Yau cross-section, twisted bilayer graphene as a scanning tunnelling microscope would see it, and Hamilton's Icosian game. Every image comes out of a few hundred lines of NumPy (the 3D ones are shaded off-screen with VTK through PyVista), so an avatar is a command plus a palette, and changing the palette is changing a list of hex colours. Everything is composed for GitHub's circular crop, checked down to 20 px, and saved below GitHub's 1 MB upload limit.
| Avatar | What you are looking at | Command |
|---|---|---|
| Hopf fibration | Circles of |
mathavatars hopf celadon |
| Calabi–Yau | The complex curve |
mathavatars calabi-yau sunset |
| Moiré | Two graphene layers twisted by 6.01°, the commensurate |
mathavatars moire ember |
| Quasicrystal | The same bilayer twisted by 30°. The lattices never realign; the pattern is a dodecagonal (12-fold) quasicrystal. | mathavatars quasicrystal gold |
| Icosian game | A Hamiltonian cycle on the dodecahedron, on its Schlegel diagram. The cycle is not hand-placed: all 30 are found by exhaustive search and reduced to the 3 classes that differ under the symmetries of the diagram. | mathavatars hamilton |
The 512 px files in gallery/ can be used as they are.
pip install git+https://github.com/HamsterPark/math-avatarsor clone the repository and pip install -e .. Python ≥ 3.10; the dependencies are NumPy, Pillow
and PyVista. The two 3D avatars need an OpenGL context (any desktop GPU, or Mesa/xvfb on a headless
machine); the moiré, quasicrystal and Icosian avatars are pure NumPy.
mathavatars palettes # what is available
mathavatars hopf celadon copper # -> hopf_celadon.png, hopf_copper.png (1024 px)
mathavatars hopf --all --outdir out # all 15 palettes; the tube mesh is built once
mathavatars calabi-yau aurora --size 512
mathavatars moire gold
mathavatars quasicrystal
mathavatars hamilton --cycle 1 # the three symmetry classes are 0, 1, 2
mathavatars preview out/*.png -o preview.pngpython -m mathavatars … works too. Then: GitHub → Settings → Public profile → Profile picture.
preview shows each avatar the way GitHub does – cropped to a circle at 260, 64, 40 and 20 px, on
the dark and the light theme. If it does not read at 20 px it is not an avatar yet:
Most palettes are taken from a material rather than from a colour wheel, which keeps the hue range
narrow and lets lightness do the work: celadon glaze with a cinnabar seal, bronze patina, polished
and oxidised copper, amber, persimmon dye with indigo, malachite and azurite with old gold, a
deep-sea chart, a lithograph, instrument brass, an old chemistry lab. dusk, twilight, berry
and rainbow are the spectral ones. The Calabi–Yau palettes are sunset, aurora and the
view-dependent pearl; the graphene palettes are ember, lagoon and gold.
A Hopf palette is a gradient along the family of fibres (position 0 is the first fibre of the arc, 1 the last) plus the badge colours and a surface finish:
from mathavatars import hopf
mine = dict(
# colour stops, interpolated in OKLab, and where each one sits along the arc
stops=["#f2e9d8", "#d9a441", "#8c3b2a", "#2b1d2a", "#2f6f6a"],
pos=[0.00, 0.20, 0.60, 0.956, 1.00],
# past 0.956 only torus 2 (the outer one) keeps following the gradient
warm_from=0.956,
warm_layers=(2,),
# badge colour and the glow behind the object
bg="#10151d",
halo="#1d3146",
# "satin" or "gloss", see mathavatars.render.FINISHES
finish="satin",
)
hopf.make(mine, "hopf_mine.png")warm_from / warm_layers keep an accent on the outer torus: the inner tori stop following the
gradient at warm_from and hold that colour. With 24 fibres the last-but-one sits at 22/23 = 0.956,
so a stop there followed by the accent at 1.0 gives exactly one accent ring.
- The circle comes first. The camera is fitted to the smallest circle enclosing the projected mesh and the object fills 90 % of it, so nothing is lost to GitHub's crop.
- One colour per fibre. Colour encodes where a fibre sits in the family and never varies along a tube, so at small sizes the eye sees bundles of circles instead of coloured plastic.
- Gradients live in OKLab, where a straight line between two colours keeps an even lightness ramp instead of sagging into grey; stops are placed so that lightness, not hue, carries the form at 20 px.
- One accent, on the outer torus only, and the fibre before it takes the darkest shade of the main hue: mixing complementary colours (pine → cinnabar, cognac → petrol) would otherwise put a muddy ring right next to the accent.
- The badge is
#111827-ish, close to GitHub's dark canvas#0d1117but not equal to it: the disc stays faintly visible in dark mode and is a complete dark badge in light mode. The halo fades out before the rim, so the rim is exactly that colour. - Satin, not plastic: a broad, faint, slightly warm highlight with a warm key and a cool fill light reads as glaze, stone or metal.
- Under 1 MB. GitHub rejects larger profile pictures;
save_avatarsteps the edge length down until the PNG fits.
Hopf fibration. Over the point
of
Calabi–Yau. Hanson's parametrisation of
Twisted bilayer graphene. Each layer is the first-harmonic honeycomb density
so
Icosian game. The dodecahedral graph has 30 Hamiltonian cycles. The symmetry group of its
Schlegel diagram (
The test-suite checks this mathematics rather than pixels: every fibre lies over one point of
Three pitfalls worth knowing about:
- SSAO banding. VTK's ambient-occlusion pass keeps view-space positions in half floats. With the camera far from the mesh the depth is quantised and the occlusion term shows wood-grain contours. The camera projection is parallel, so its distance is free – keep it just outside the bounding sphere.
- Seams.
smooth_shading=Truein PyVista recomputes normals per mesh piece, which shows the seams between the 25 Calabi–Yau patches. Set the exact normals as the active normals and switch the actor to Phong interpolation instead. - Pinholes. The parameter grid must contain the branch points as vertices (odd grid size, grid refined towards them), otherwise every branch point is a small hole in the mesh.
pip install -e ".[dev]"
ruff check . && ruff format --check .
pytest # mathematics, palettes, CLI, image output
MATHAVATARS_RENDER_TESTS=1 pytest -m render # end-to-end VTK renders, needs OpenGL
python docs/make_figures.py # rebuild the README figures from gallery/- H. Hopf, Über die Abbildungen der dreidimensionalen Sphäre auf die Kugelfläche, Math. Ann. 104 (1931). The layout – fibres over circles of latitude, coloured by longitude – follows the well-known visualisation by Niles Johnson.
- A. J. Hanson, A construction for computer visualization of certain complex curves, Notices of the AMS 41 (9), 1156–1163 (1994).
- S. J. Ahn et al., Dirac electrons in a dodecagonal quasicrystal, Science 361, 782–786 (2018).
- W. R. Hamilton, The Icosian Game (1857).
- B. Ottosson, A perceptual color space for image processing (2020).
MIT, images included – use one as your avatar if you like, or better, make your own palette.