Статья

ASCII-портрет в GitHub-профиле (README)

Python · Pillow · 12 июля 2026 · 6 мин
Пример ASCII-портрета с инфопанелью

GitHub рендерит README профиля из репозитория, имя которого совпадает с вашим ником (например, e1teck/e1teck). Раскрасить текст в markdown нельзя — GitHub вырезает inline-стили при санитайзинге. Единственный способ получить цветной моноширинный арт — вставить его как SVG-картинку. Чтобы он выглядел правильно и в тёмной, и в светлой теме, делают два SVG и переключают их тегом <picture>.

Три составляющих:

Шаг 1. Фото → ASCII

Уменьшаем фото до символьной сетки и заменяем каждый пиксель глифом подходящей «плотности». Для тёмной темы яркие пиксели становятся плотными глифами (@M$%); для светлой — наоборот, плотные глифы получают тёмные пиксели.

Ключевые приёмы, без которых получается каша:

Минимальный пример:

from PIL import Image, ImageOps, ImageEnhance, ImageFilter

W = 116                       # ширина в символах
ramp = " .'`^,:;i!|(fjrxnkmpqwZO%$M@"   # от разреженных к плотным

img = Image.open("photo.jpg")
g = ImageOps.grayscale(img)
g = ImageEnhance.Contrast(g).enhance(1.45)
g = g.filter(ImageFilter.UnsharpMask(radius=3, percent=200))
H = int(W * img.height / img.width * 0.48)
g = g.resize((W, H))

px = g.load()
for y in range(H):
    line = ""
    for x in range(W):
        v = px[x, y] / 255            # светлая тема: v = 1 - v
        line += ramp[int(v * (len(ramp) - 1))]
    print(line)

Шаг 2. ASCII → SVG

SVG даёт три «суперсилы», которых нет у обычного markdown-текста:

Каждая строка — элемент <text> с xml:space="preserve" (иначе браузер схлопнет пробелы); цветные фрагменты — <tspan class="…">:

<svg xmlns="http://www.w3.org/2000/svg" width="1081" height="709" viewBox="0 0 1081 709">
  <style>
    text { font-family: 'SFMono-Regular', Consolas, Menlo, monospace;
           font-size: 14px; white-space: pre }
    .art   { font-size: 7.5px; fill: #c9d1d9 }
    .label { fill: #ffa657 }
    .value { fill: #79c0ff }
    .barF  { fill: #58a6ff }
  </style>
  <rect width="100%" height="100%" fill="#0d1117" rx="8"/>
  <text x="14" y="27" xml:space="preserve" textLength="522.5"
        lengthAdjust="spacingAndGlyphs"><tspan class="label">. OS:</tspan><tspan class="value"> macOS, Linux</tspan></text>
  <!-- … строки арта и панели … -->
</svg>

Прогресс-бары — это просто глифы и в tspan с разными классами:

. Python:................... ██████████████████░░ 90%

Цвета берём из палитры GitHub: тёмная тема — фон #0d1117, текст #c9d1d9, синий #79c0ff, оранжевый #ffa657; светлая — фон #ffffff, текст #24292f, синий #0550ae, красный #cf222e.

Шаг 3. Публикация

<a href="https://a1eks.com">
  <picture>
    <source media="(prefers-color-scheme: dark)"
            srcset="https://raw.githubusercontent.com/e1teck/a1eks.com/master/img/dark_mode.svg">
    <img alt="Aleksei Ryzhkov"
         src="https://raw.githubusercontent.com/e1teck/a1eks.com/master/img/light_mode.svg">
  </picture>
</a>

Частые грабли: инвертированный вид в светлой теме, шум фона из-за неполной заливки, «съеденная» футболка при заливке, рассинхрон из-за шрифта, задержки кэша картинок у GitHub и неверная ветка в URL.

PythonGitHubPillowSVG

Все статьи · Обсудить проект