Mint monogram
after Linux Mint, Linux Mint logo1
Linux Mint’s lm as a construction drawing, its strokes laid on a half-stroke grid and their edges run on as guide lines, with the l filled in.
Made with Claude Opus 5.5
Unofficial fan tribute, not affiliated with or endorsed by Linux Mint. Linux Mint and its characters are trademarks of their owners. Non-commercial; contact [email protected] for takedown.
- Technique
- technical drawing
- Shape
- Any screen
- Added
- 28 September 2026
Colours
- #1C1B1Abackground
- #DAD8CEforeground
- #CF6A4Caccent
Export
Notes
The mark is one stroke width throughout, and its straight runs and arc centres all fall on a grid at half that width, as in the project’s own source file, which draws the logo as a stroked line. A dash-dot line follows that stroke down the middle. Arcs are shaded a step apart from straight runs, and two arrows point at the radius of an arch and of a corner. The dash-dot circle is where the ring of the round badge runs; guide lines stop at the mark, so the spaces inside the letters stay clear.
Sources
- Linux Mint, Linux Mint logo. ↑
Source code
wallpapers/mint-monogram/design.py, 142 lines
"""The Linux Mint monogram as a construction drawing: strokes of one width on a half-stroke grid, their edges run on as fading guide lines, the l picked out."""
from shapely import LineString, Point, Polygon, box
from shapely.geometry.base import BaseGeometry
from shapely.ops import unary_union
from walldye import ACCENT, ACCENT_3, BG, BG_ALT, UI, UI_ALT, Canvas, P, Vec, design, polar
from walldye.geom import parts
H = 26 # half the stroke width, the grid unit
FADE = 620 # construction lines dissolve into the background by this radius
DASHDOT = (24, 6, 3, 6)
ARROW, ARROW_W = 13, 4.2
# The official badge on the half-stroke grid, origin at the top of the l's outer edge. The mark
# is 14 by 13; its ring runs on a circle 11.8 from the middle of that box.
SIZE = Vec(14, 13)
RING = 11.8
UPRIGHT = (0, 0, 2, 9) # the l, down to where its corner begins
RUNS = ((4, 11, 10, 13), (12, 4, 14, 9), (8, 4, 10, 9), (4, 4, 6, 9))
# arcs as (centre, inner radius, outer radius, from, to) in degrees, clockwise from east
ARCS = (
(Vec(4, 9), 2, 4, 90, 180),
(Vec(10, 9), 2, 4, 0, 90),
(Vec(11, 4), 1, 3, 180, 360),
(Vec(7, 4), 1, 3, 180, 360),
)
# the grid lines that straight edges lie on, plus y = 4, where the arches meet their legs
EDGES_X = (0, 2, 4, 6, 8, 10, 12, 14)
EDGES_Y = (0, 4, 9, 11, 13)
def band(c: Vec, r0: float, r1: float, a0: float, a1: float) -> Polygon:
"""An annular sector as a polygon, 64 steps per quarter turn."""
n = max(2, round(64 * (a1 - a0) / 90))
outer = [polar(c, r1, deg=a0 + (a1 - a0) * k / n) for k in range(n + 1)]
inner = [polar(c, r0, deg=a1 - (a1 - a0) * k / n) for k in range(n + 1)]
return Polygon(outer + inner)
def strands(g: BaseGeometry) -> list[LineString]:
"""The line parts of `g` longer than 2 units; shorter ones are boolean slivers."""
return [q for q in parts(g) if isinstance(q, LineString) and q.length > 2]
@design(aspects="any")
def draw(s: Canvas) -> None:
c = s.pick(landscape=(31 / 48, 0.5), portrait=(0.5, 0.42))
o = c - SIZE * (H / 2)
def at(x: float, y: float) -> Vec:
return o + Vec(x, y) * H
def rect(r: tuple[float, float, float, float]) -> Polygon:
return box(*at(r[0], r[1]), *at(r[2], r[3]))
arcs = [band(at(*a[0]), a[1] * H, a[2] * H, a[3], a[4]) for a in ARCS]
mark = unary_union([rect(UPRIGHT), *(rect(r) for r in RUNS), *arcs])
clear = mark.buffer(2.5, join_style="mitre")
field = Point(c.x, c.y).buffer(FADE, quad_segs=128)
ring = RING * H
def outside(g: BaseGeometry) -> list[LineString]:
"""The strands of `g` off the mark, less any that bridge two parts of it (a pocket)."""
keep = []
for q in strands(g.difference(clear)):
ends = [Point(q.coords[0]), Point(q.coords[-1])]
if not all(e.distance(mark) < 4 for e in ends):
keep.append(q)
return keep
# construction: those lines extended to the fading field
lines = P()
for x in EDGES_X:
a, b = at(x, 0), at(x, 13)
for q in outside(
LineString([(a.x, a.y - 2 * FADE), (b.x, b.y + 2 * FADE)]).intersection(field)
):
lines.poly(q.coords)
for y in EDGES_Y:
a, b = at(0, y), at(14, y)
for q in outside(
LineString([(a.x - 2 * FADE, a.y), (b.x + 2 * FADE, b.y)]).intersection(field)
):
lines.poly(q.coords)
fade = s.radial_gradient([(0, BG_ALT), (ring * 1.2 / FADE, BG_ALT), (1, BG)], c, FADE)
s.stroke(lines, fade, 1.2)
# centre lines through the middle of the mark, and the ring's circle
cl = P()
for q in outside(LineString([(c.x - ring - 60, c.y), (c.x + ring + 60, c.y)])):
cl.poly(q.coords)
for q in outside(LineString([(c.x, c.y - ring - 60), (c.x, c.y + ring + 60)])):
cl.poly(q.coords)
s.stroke(cl.circle(c, ring), UI, 1.2, dash=DASHDOT)
# dimensions: the overall width below, one stroke width across the top of the l
ext, dl, heads = P(), P(), P()
x0, x1 = at(0, 0).x, at(14, 0).x
yd = at(0, 13).y + 64
for x in (x0, x1):
ext.M(x, at(0, 9).y + 14).V(yd + 12)
dl.M(x0, yd).H(x1)
heads.arrowhead((x1, yd), ARROW, deg=0, width=ARROW_W)
heads.arrowhead((x0, yd), ARROW, deg=180, width=ARROW_W)
xa, xb, yt = at(0, 0).x, at(2, 0).x, o.y
yc = yt - 40
for x in (xa, xb):
ext.M(x, yt - 12).V(yc - 12)
dl.M(xa - 34, yc).H(xa).M(xb, yc).H(xb + 34)
heads.arrowhead((xa, yc), ARROW, deg=0, width=ARROW_W)
heads.arrowhead((xb, yc), ARROW, deg=180, width=ARROW_W)
# radii: a leader from the ring onto the outer edge of one arch and one corner, on the line
# through the arc's centre
for (ctr, _, r1, _, _), deg in ((ARCS[2], -45.0), (ARCS[1], 45.0)):
a = at(*ctr)
d = polar(Vec(0, 0), 1, deg=deg)
b = d.dot(a - c)
t = -b + (b * b - (abs(a - c) ** 2 - ring * ring)) ** 0.5
tip = a + d * (r1 * H)
dl.M(tip + d * 6).L(a + d * (t - 18))
heads.arrowhead(tip, ARROW, deg=deg + 180, width=ARROW_W)
s.stroke(ext, UI, 1.2)
s.stroke(dl, UI_ALT, 1.2)
s.fill(heads, UI_ALT)
# the mark: arcs in UI under the straight runs in UI_ALT, the l in ACCENT
s.fill(P().shape(mark), UI)
runs = P()
for r in RUNS:
runs.rect(*at(r[0], r[1]), *(Vec(r[2] - r[0], r[3] - r[1]) * H))
s.fill(runs, UI_ALT)
s.fill(P().rect(*at(0, 0), 2 * H, 9 * H), ACCENT)
# the stroke's centreline, as the source artwork draws it; ACCENT_3 over the l
cen = P()
cen.M(at(1, 9)).A(3 * H, 3 * H, 0, 0, 0, *at(4, 12)).L(at(10, 12))
cen.A(3 * H, 3 * H, 0, 0, 0, *at(13, 9)).L(at(13, 4))
cen.A(2 * H, 2 * H, 0, 0, 0, *at(11, 2)).A(2 * H, 2 * H, 0, 0, 0, *at(9, 4)).L(at(9, 9))
cen.M(at(9, 4)).A(2 * H, 2 * H, 0, 0, 0, *at(7, 2)).A(2 * H, 2 * H, 0, 0, 0, *at(5, 4))
cen.L(at(5, 9))
s.stroke(cen, BG, 1.2, dash=DASHDOT)
s.stroke(P().M(at(1, 0)).L(at(1, 9)), ACCENT_3, 1.2, dash=DASHDOT)"""The Linux Mint monogram as a construction drawing: strokes of one width on a half-stroke grid, their edges run on as fading guide lines, the l picked out."""
from shapely import LineString, Point, Polygon, box
from shapely.geometry.base import BaseGeometry
from shapely.ops import unary_union
from walldye import ACCENT, ACCENT_3, BG, BG_ALT, UI, UI_ALT, Canvas, P, Vec, design, polar
from walldye.geom import parts
H = 26 # half the stroke width, the grid unit
FADE = 620 # construction lines dissolve into the background by this radius
DASHDOT = (24, 6, 3, 6)
ARROW, ARROW_W = 13, 4.2
# The official badge on the half-stroke grid, origin at the top of the l's outer edge. The mark
# is 14 by 13; its ring runs on a circle 11.8 from the middle of that box.
SIZE = Vec(14, 13)
RING = 11.8
UPRIGHT = (0, 0, 2, 9) # the l, down to where its corner begins
RUNS = ((4, 11, 10, 13), (12, 4, 14, 9), (8, 4, 10, 9), (4, 4, 6, 9))
# arcs as (centre, inner radius, outer radius, from, to) in degrees, clockwise from east
ARCS = (
(Vec(4, 9), 2, 4, 90, 180),
(Vec(10, 9), 2, 4, 0, 90),
(Vec(11, 4), 1, 3, 180, 360),
(Vec(7, 4), 1, 3, 180, 360),
)
# the grid lines that straight edges lie on, plus y = 4, where the arches meet their legs
EDGES_X = (0, 2, 4, 6, 8, 10, 12, 14)
EDGES_Y = (0, 4, 9, 11, 13)
def band(c: Vec, r0: float, r1: float, a0: float, a1: float) -> Polygon:
"""An annular sector as a polygon, 64 steps per quarter turn."""
n = max(2, round(64 * (a1 - a0) / 90))
outer = [polar(c, r1, deg=a0 + (a1 - a0) * k / n) for k in range(n + 1)]
inner = [polar(c, r0, deg=a1 - (a1 - a0) * k / n) for k in range(n + 1)]
return Polygon(outer + inner)
def strands(g: BaseGeometry) -> list[LineString]:
"""The line parts of `g` longer than 2 units; shorter ones are boolean slivers."""
return [q for q in parts(g) if isinstance(q, LineString) and q.length > 2]
@design(aspects="any")
def draw(s: Canvas) -> None:
c = s.pick(landscape=(31 / 48, 0.5), portrait=(0.5, 0.42))
o = c - SIZE * (H / 2)
def at(x: float, y: float) -> Vec:
return o + Vec(x, y) * H
def rect(r: tuple[float, float, float, float]) -> Polygon:
return box(*at(r[0], r[1]), *at(r[2], r[3]))
arcs = [band(at(*a[0]), a[1] * H, a[2] * H, a[3], a[4]) for a in ARCS]
mark = unary_union([rect(UPRIGHT), *(rect(r) for r in RUNS), *arcs])
clear = mark.buffer(2.5, join_style="mitre")
field = Point(c.x, c.y).buffer(FADE, quad_segs=128)
ring = RING * H
def outside(g: BaseGeometry) -> list[LineString]:
"""The strands of `g` off the mark, less any that bridge two parts of it (a pocket)."""
keep = []
for q in strands(g.difference(clear)):
ends = [Point(q.coords[0]), Point(q.coords[-1])]
if not all(e.distance(mark) < 4 for e in ends):
keep.append(q)
return keep
# construction: those lines extended to the fading field
lines = P()
for x in EDGES_X:
a, b = at(x, 0), at(x, 13)
for q in outside(
LineString([(a.x, a.y - 2 * FADE), (b.x, b.y + 2 * FADE)]).intersection(field)
):
lines.poly(q.coords)
for y in EDGES_Y:
a, b = at(0, y), at(14, y)
for q in outside(
LineString([(a.x - 2 * FADE, a.y), (b.x + 2 * FADE, b.y)]).intersection(field)
):
lines.poly(q.coords)
fade = s.radial_gradient([(0, BG_ALT), (ring * 1.2 / FADE, BG_ALT), (1, BG)], c, FADE)
s.stroke(lines, fade, 1.2)
# centre lines through the middle of the mark, and the ring's circle
cl = P()
for q in outside(LineString([(c.x - ring - 60, c.y), (c.x + ring + 60, c.y)])):
cl.poly(q.coords)
for q in outside(LineString([(c.x, c.y - ring - 60), (c.x, c.y + ring + 60)])):
cl.poly(q.coords)
s.stroke(cl.circle(c, ring), UI, 1.2, dash=DASHDOT)
# dimensions: the overall width below, one stroke width across the top of the l
ext, dl, heads = P(), P(), P()
x0, x1 = at(0, 0).x, at(14, 0).x
yd = at(0, 13).y + 64
for x in (x0, x1):
ext.M(x, at(0, 9).y + 14).V(yd + 12)
dl.M(x0, yd).H(x1)
heads.arrowhead((x1, yd), ARROW, deg=0, width=ARROW_W)
heads.arrowhead((x0, yd), ARROW, deg=180, width=ARROW_W)
xa, xb, yt = at(0, 0).x, at(2, 0).x, o.y
yc = yt - 40
for x in (xa, xb):
ext.M(x, yt - 12).V(yc - 12)
dl.M(xa - 34, yc).H(xa).M(xb, yc).H(xb + 34)
heads.arrowhead((xa, yc), ARROW, deg=0, width=ARROW_W)
heads.arrowhead((xb, yc), ARROW, deg=180, width=ARROW_W)
# radii: a leader from the ring onto the outer edge of one arch and one corner, on the line
# through the arc's centre
for (ctr, _, r1, _, _), deg in ((ARCS[2], -45.0), (ARCS[1], 45.0)):
a = at(*ctr)
d = polar(Vec(0, 0), 1, deg=deg)
b = d.dot(a - c)
t = -b + (b * b - (abs(a - c) ** 2 - ring * ring)) ** 0.5
tip = a + d * (r1 * H)
dl.M(tip + d * 6).L(a + d * (t - 18))
heads.arrowhead(tip, ARROW, deg=deg + 180, width=ARROW_W)
s.stroke(ext, UI, 1.2)
s.stroke(dl, UI_ALT, 1.2)
s.fill(heads, UI_ALT)
# the mark: arcs in UI under the straight runs in UI_ALT, the l in ACCENT
s.fill(P().shape(mark), UI)
runs = P()
for r in RUNS:
runs.rect(*at(r[0], r[1]), *(Vec(r[2] - r[0], r[3] - r[1]) * H))
s.fill(runs, UI_ALT)
s.fill(P().rect(*at(0, 0), 2 * H, 9 * H), ACCENT)
# the stroke's centreline, as the source artwork draws it; ACCENT_3 over the l
cen = P()
cen.M(at(1, 9)).A(3 * H, 3 * H, 0, 0, 0, *at(4, 12)).L(at(10, 12))
cen.A(3 * H, 3 * H, 0, 0, 0, *at(13, 9)).L(at(13, 4))
cen.A(2 * H, 2 * H, 0, 0, 0, *at(11, 2)).A(2 * H, 2 * H, 0, 0, 0, *at(9, 4)).L(at(9, 9))
cen.M(at(9, 4)).A(2 * H, 2 * H, 0, 0, 0, *at(7, 2)).A(2 * H, 2 * H, 0, 0, 0, *at(5, 4))
cen.L(at(5, 9))
s.stroke(cen, BG, 1.2, dash=DASHDOT)
s.stroke(P().M(at(1, 0)).L(at(1, 9)), ACCENT_3, 1.2, dash=DASHDOT)
Run it yourself
$ git clone https://github.com/nickolaj-jepsen/walldye && cd walldye$ uv run walldye render mint-monogram --theme fireproof -o mint-monogram-fireproof-16x9.svg