Skip to main content

Generate self-contained HTML mock-ups of chat conversations styled like popular messaging & dating apps.

Project description

chatgen

Generate self-contained HTML mock-ups of chat conversations styled exactly like popular platforms — messengers (iMessage, WhatsApp, Telegram, Instagram DM, Messenger, Signal, Discord, Snapchat, VK, Slack, LINE, X/Twitter DM, WeChat, Viber) and dating apps (Tinder, Bumble, Hinge). Feed it a dialog (participants + messages with timestamps), pick a platform, device, and theme, and get back ready-to-open HTML — or a PNG screenshot.

By default the output is a clean, edge-to-edge screenshot (just the screen, no device bezel). The phone frame is an opt-in.

Install

pip install chatgen          # or:  uv add chatgen

# optional, for PNG export:
pip install "chatgen[screenshots]"
playwright install chromium

Quickstart

from datetime import datetime
from chatgen import Generator, Dialog, Participant, Message, Text, Image
from chatgen import Platform, DeviceView, Theme, MessageStatus

me  = Participant(id="me",  name="You", is_self=True)
her = Participant(id="her", name="Alex Rivera", subtitle="online")

dialog = Dialog(participants=[me, her], messages=[
    Message(her, datetime(2026, 5, 31, 9, 40), Text("Hey! How's it going? 😊")),
    Message(me,  datetime(2026, 5, 31, 9, 41), Text("Pretty good! You?"),
            status=MessageStatus.READ),
    Message(her, datetime(2026, 5, 31, 9, 42), Image(src="alex_selfie.jpg")),
])

gen = Generator()

# 1) Clean screenshot HTML (default — no device frame)
html = gen.generate(dialog, platform=Platform.WHATSAPP,
                    view=DeviceView.IPHONE_15_PRO, theme=Theme.DARK)
open("chat.html", "w").write(html)

# 2) With the phone bezel (opt-in)
html_framed = gen.generate(dialog, platform=Platform.IMESSAGE,
                           view=DeviceView.IPHONE_15_PRO, theme=Theme.LIGHT,
                           show_frame=True)

# 3) PNG screenshot (needs the 'screenshots' extra)
gen.generate_png(dialog, platform=Platform.TELEGRAM,
                 view=DeviceView.IPHONE_15_PRO, theme=Theme.LIGHT,
                 path="chat.png", scale=2)

API

Generator().generate(dialog, platform, view, theme, **kwargs) -> str   # HTML
Generator().generate_png(dialog, platform, view, theme, *, path=None, scale=2, **kwargs) -> bytes
  • dialog — a Dialog (see model below).
  • platform — a Platform enum member or a registry-key string.
  • view — a DeviceView: IPHONE_15_PRO, IPHONE_SE, PIXEL_8, ANDROID, WEB.
  • themeTheme.LIGHT or Theme.DARK.

Common kwargs

kwarg default meaning
show_frame False draw the phone bezel + drop shadow (screenshot-first by default)
show_status_bar True render the OS status bar (clock/signal/battery)
clock "9:41" status-bar time
battery 100 status-bar battery percent
typing False show a "typing…" indicator bubble for the other person
wallpaper None chat background — a named preset ("sunset", "ocean", … see list_wallpapers()) or any raw CSS color/gradient — WhatsApp & Telegram
animate False when the HTML page opens, reveal messages one-by-one (fade + slide-up, scrolling to follow). Opt-in; works on every platform & device. generate_png ignores it (a still can't animate).
animate_start 300 ms before the first message appears (with animate)
animate_delay 450 ms between each message appearing (with animate)

animate degrades gracefully: with JavaScript disabled or prefers-reduced-motion, all messages show immediately.

Message(reply_to=...) renders an inline reply quote inside the bubble.

Devices (view): iphone_15_pro, iphone_16_pro, iphone_15_pro_max, iphone_se, pixel_8, pixel_9, galaxy_s24, android, web.

Platform-specific kwargs

platform kwargs
imessage show_contact_avatar
whatsapp show_wallpaper, is_group
telegram accent (hex color)
tinder show_match_banner (uses dialog.meta["matched_at"])
bumble show_expiry, expiry_label
hinge reads dialog.meta["liked"] for the context card

Data model

Participant(id, name, avatar=None, is_self=False, subtitle=None, age=None, distance=None, bio=None)
Dialog(participants, messages, meta={})
Message(sender, timestamp, content, status=MessageStatus.SENT, reactions=[], reply_to=None)
Reaction(emoji, by)

Content types: Text(body), Image(src, caption), Voice(duration_seconds, transcript), Sticker(src), GIF(src, caption), File(name, size), Location(label, map_image), System(text).

Avatars & images (avatar / src) accept any of: an http(s) URL, a local file path (embedded as a data-URI so the HTML stays self-contained), an existing data: URI, or None (a deterministic initials-on-color placeholder is generated).

Graceful degradation: each platform declares which content types it natively renders; unsupported types fall back to a readable bubble (e.g. Tinder renders a Voice as "🎤 Voice message") instead of breaking.

Supported platforms

from chatgen import list_platforms
list_platforms()
# ['bumble', 'discord', 'hinge', 'imessage', 'instagram', 'line', 'messenger',
#  'signal', 'slack', 'snapchat', 'telegram', 'tinder', 'twitter', 'viber',
#  'vk', 'wechat', 'whatsapp']

from chatgen import list_wallpapers
list_wallpapers()  # 122 named wallpaper themes for the `wallpaper` kwarg
# mood gradients:   sunset, ocean, dusk, forest, rose, midnight, peach, lavender,
#                   mint, graphite, fire, ice, grape, coral, emerald, twilight,
#                   sakura, steel, gold, denim, noir, candy
# Telegram-style:   aurora, tropic, berry, lagoon, blossom, instaglow, seafoam,
# (multi-point      peony, marine, nebula, citrus, orchid, ember, glacier,
#  "freeform")      meadow, rosegold, cobalt
# solid colors:     sand, sage, fog, linen, clay, stone, ink, espresso, mist,
#                   almond, moss, dove, pine, navy, wine, slateblue
# editor/aesthetic: dracula, nord, solarized, solarized_dark, gruvbox, monokai,
#                   catppuccin, tokyonight, onedark, github_dark, vaporwave,
#                   synthwave, matrix, terminal, paper, kraft, chalkboard
# css patterns:     dots, dots_dark, grid, cross, blueprint, carbon, graph, stars
# image patterns:   doodle, polka, hearts, triangles, hexagons, waves, memphis,
# (SVG, embedded)   confetti, circuit, starfield, zigzag, seigaiha, plus,
#                   diamonds, bubbles, leafy, herringbone, rings, crosshatch2,
#                   terrazzo, scales, stars_small
# photo wallpapers: photo_mountain, photo_forest, photo_ocean, photo_city,
# (JPEG, embedded)  photo_desert, photo_dunes, photo_leaves, photo_sky,
#                   photo_coast, photo_woods, photo_alps, photo_pines,
#                   photo_planks, photo_marina, photo_harvest, photo_canal,
#                   photo_village, photo_trail, photo_lights, photo_greenery

Image wallpapers (SVG patterns + photos, in chatgen/assets/wallpapers/) are base64-embedded into the output, so the HTML stays self-contained. Preview them all on one conversation: python examples/wallpaper_grid.py.

(Or pass any raw CSS background value — color, gradient, or url(...) — directly.)

Adding a platform

Platforms are plugins. Create a renderer, a screen.html, and a style.css:

# chatgen/platforms/<group>/<name>/renderer.py
from chatgen.platforms.base import Capabilities, PlatformRenderer
from chatgen.registry import register_platform

@register_platform("signal")
class SignalRenderer(PlatformRenderer):
    capabilities = Capabilities()          # toggle off unsupported content types
    kwargs_schema = {"accent": "#3a76f0"}  # platform-specific options + defaults

    def build_context(self, dialog, device, theme, **kwargs):
        other = dialog.others[0] if dialog.others else None
        return {"header_name": other.name if other else "Signal"}

Templates live in <name>/templates/screen.html (import shared macros with {% import "macros.html" as m %} and use m.content_block(msg, caps, "<prefix>")), styled by <name>/style.css. Register the module in chatgen/platforms/__init__.py.

Examples gallery

python examples/generate_gallery.py
# open examples/out/index.html

Development

Managed with uv:

uv sync              # create .venv with the project + dev group
uv run pytest -q     # run the test suite
uv build             # build sdist + wheel into dist/
uvx twine check dist/*

CI runs the suite on Python 3.10–3.13 (.github/workflows/ci.yml).

Releasing (PyPI)

Publishing is automated via PyPI Trusted Publishing (OIDC — no API token stored).

  1. One-time: on PyPI, add a GitHub publisher for project chatgen → owner RuNeira, repo screenshots-generator, workflow publish.yml, environment pypi.
  2. Bump version in pyproject.toml, commit.
  3. Tag and create a GitHub Release (vX.Y.Z). The publish.yml workflow verifies the tag matches the version, builds, and uploads to PyPI.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

chatgen-0.1.0.tar.gz (1.8 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

chatgen-0.1.0-py3-none-any.whl (1.9 MB view details)

Uploaded Python 3

File details

Details for the file chatgen-0.1.0.tar.gz.

File metadata

  • Download URL: chatgen-0.1.0.tar.gz
  • Upload date:
  • Size: 1.8 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for chatgen-0.1.0.tar.gz
Algorithm Hash digest
SHA256 caf11595b51e473a6ea81372b4cb9f42661f4260be751709763386c5edb54170
MD5 3a1ff1b1d4cd72171d7c26cfd18680a9
BLAKE2b-256 fc0e328899d3c03b55819f6f45763ec0fbfa2f2f73634ddbc24e5c47b22091c7

See more details on using hashes here.

Provenance

The following attestation bundles were made for chatgen-0.1.0.tar.gz:

Publisher: publish.yml on RuNeira/screenshots-generator

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file chatgen-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: chatgen-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 1.9 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for chatgen-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2913d0bfc96535ba469ffb912ba56301276c680e5f01ea4ee17ed78727d8bde3
MD5 e5b5de8230b2fc5947c4c773cca9f9e9
BLAKE2b-256 363dde5b811e7f00a2a5eafc4f50834314aee5f25f5e6ebb85e2cb331708581a

See more details on using hashes here.

Provenance

The following attestation bundles were made for chatgen-0.1.0-py3-none-any.whl:

Publisher: publish.yml on RuNeira/screenshots-generator

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page