Skip to main content

Render tweets as PNG/JPG images using Playwright

Project description

tw2img

A tool that renders tweets as PNG images using Playwright (headless Chromium). Works with usernames, tweet IDs, URLs, local JSON files, or stdin.

This style is based on nitter using Midnight theme as default.

image grid birdwatch note and thread

Installation

From PyPI (recommended)

pip install tw2img
playwright install chromium

From source

git clone https://github.com/cmj/tw2img.git
cd tw2img
pip install playwright
playwright install chromium

Quick Start

1. Guest Mode (no auth token required, missing context for replies)

# By @username (fetch latest tweet)
tw2img @AP --guest
# By @username, fetch the 3rd most recent tweet
tw2img @AP 3 --guest
# By tweet ID
tw2img 2041557036274475228 --guest
# By tweet URL
tw2img https://x.com/NASA/status/2041557036274475228 --guest

When running from source, replace tw2img with python tw2img.py.

Here is a list of popular Twitter accounts sorted by most recent, and useful for guest access: https://github.com/cmj/twitter-tools/wiki/RSS%E2%80%90Friendly

2. Authenticated Mode (full thread + reply data)

You need your Twitter auth tokens. Export them as environment variables:

export TWITTER_AUTH_TOKEN="your_auth_token_here"
export TWITTER_CSRF_TOKEN="your_ct0_token_here"
# alternative, only requires setting auth_token
export TWITTER_CSRF_TOKEN=$(openssl rand -hex 16)

Then run:

tw2img 2054583770045386950

Where to find tokens: Open browser devtools, network tab, any x.com request, select cookies tab - auth_token and ct0.


Basic Options

Option Description
@user Fetch latest tweet from this user
--user <name> Same as above
--light Use light theme (default is dark)
--no-source Hide the "Twitter for iPhone" source text
--no-context Show only the focal tweet, no thread/replies
--no-retina Disable 2x retina rendering (smaller file)
--full-stats Show full numbers instead of abbreviated (e.g. 12,345 instead of 12.3K)
--output-dir <path> Directory to save output PNG (default: current working directory)
--width 800 Set output width in pixels (default: 598)
--css theme.css File to override the theme (ex: nitter/public/css/themes/pleroma.css)
--nitter Use Nitter default theme
--html-only Print HTML to stdout instead of rendering PNG
--save-html [FILE] Save HTML instead of rendering PNG. Omit FILE to auto-name as <user>-<id>.html alongside the PNG
--view-html Shorthand for --save-html + --view: auto-save HTML and open it immediately
--imgur Upload PNG to imgur after rendering
--dump-json Print raw API JSON to stdout and exit
--trans <[SOURCE:]TARGET> Translate tweet text before rendering. Target-only (e.g. --trans en) auto-detects source; SOURCE:TARGET (e.g. --trans ja:en) sets both. Requires pip install deep-translator
--print-line Print a one-line text summary of the focal tweet to stdout
--view Automatically open the rendered output file after creation
--viewer <cmd> Specify custom viewer executable/command (e.g., viewnior, firefox, or kitty +icat {})
-c <file> Load config from a custom path (see Config below)

Config File

Options can be set as persistent defaults in a config file (INI format). Config is loaded in this order - later sources override earlier ones:

  1. ~/.config/tw2img/tw2img.conf - user default
  2. <script_dir>/tw2img.conf - next to the script, if present
  3. -c /path/to/custom.conf - explicit override
  4. Command options / flags always have highest priority

A default config is included as tw2img.conf. To install it:

mkdir -p ~/.config/tw2img
cp tw2img.conf ~/.config/tw2img/tw2img.conf

Set a default download directory in the config so you don't have to specify it each run:

[tw2img]
output_dir = ~/Pictures/tweets

If output_dir is set, all PNGs are saved there unless you pass an explicit output path (absolute or with a directory component) on the command line.

Use -c to load an alternate config for a specific run without touching your defaults:

tw2img 2054583770045386950 -c ~/work/tw2img-work.conf --light

Input Types

# @username shorthand - latest tweet
tw2img @NASA --guest

# @username shorthand - Nth most recent tweet (1-20, skips RTs and replies)
tw2img @NASA 5 --guest

# Explicit --user flag (equivalent to @username)
tw2img --user NASA --guest

# Tweet ID
tw2img 2054583770045386950 --guest

# Full URL
tw2img "https://x.com/username/status/123456789" --guest

# Local JSON file (from API)
tw2img tweet.json

# Stdin (pipe JSON)
cat tweet.json | tw2img -

Output

By default, saves as <screen_name>-<tweet_id>.png in current directory. Specify a custom filename as the argument after the input (or after the tweet index when using @username):

# Custom output with tweet ID
tw2img 2054583770045386950 --guest my_screenshot.png

# Custom output with @username shorthand
tw2img @NASA my_screenshot.png --guest

# Custom output with @username and tweet index
tw2img @NASA 3 my_screenshot.png --guest

# Open with a specific GUI viewer
tw2img @NASA --guest --view --viewer viewnior

# Render directly inline inside a supported terminal (like kitty)
tw2img @NASA --guest --view --viewer "kitty +icat {}"

# View directly in Firefox (ideal when combined with --save-html)
tw2img 2054583770045386950 --save-html tweet.html --view --viewer firefox

# Shorthand: auto-save HTML and open immediately (uses viewer from config, default firefox)
tw2img 2054583770045386950 --view-html

# Print a one-line text summary of the focal tweet to stdout
tw2img 21 --print-line --guest
@biz (Biz Stone)  just setting up my twttr |  153  4.8K  302  4.3K | Web Client | https://x.com/i/status/21

Examples

Basic screenshot with thread (dark mode):

tw2img 2054583770045386950 --guest

Latest tweet from a user:

tw2img @NASA --guest

5th most recent tweet from a user:

tw2img @NASA 5 --guest

Automatically open the snapshot after rendering:

tw2img @NASA --guest --view

Upload to imgur:

tw2img @NASA --guest --imgur

Light theme, focal tweet only:

tw2img 2054583770045386950 --guest --light --no-context

Wide screenshot without source:

tw2img 2054583770045386950 --guest --width 800 --no-source

Full stat numbers:

tw2img 2054583770045386950 --guest --full-stats

Print HTML to stdout (for inspection or debugging):

tw2img 2054583770045386950 --guest --html-only

Save HTML and open immediately:

tw2img 2054583770045386950 --guest --view-html

Save HTML to a specific file and open in Firefox:

tw2img 2054583770045386950 --guest --save-html tweet.html --view --viewer firefox

Print tweet text:

$ tw2img --print-line --guest 22

@noah (noah glass) just setting up my twttr | ↳ 86 ⇅ 3.9K ‟ 167 ♥ 3.4K | Web Client | https://x.com/i/status/22

Translate tweet before rendering:

# Install the translation dependency once
pip install deep-translator

# Auto-detect source, translate to English
tw2img 2059593901607153975 --guest --trans en

# Explicitly set source -> target (Japanese -> English)
tw2img 2059593901607153975 --guest --trans ja:en

--trans translates the tweet text (and any quoted tweet) before rendering. Use SOURCE:TARGET to specify both languages, or just TARGET to auto-detect. Language codes follow BCP-47 / ISO 639-1 (en, ja, fr, zh-CN, etc). Can also be set as a default in tw2img.conf:

[tw2img]
# translate everything not lang:en to english
trans = en

Articles

Articles are long-form content that don't render well as a PNG. The recommended approach is to save as HTML and open in a browser via --view-html:

# Preferred: auto-save HTML and open immediately
article2img --guest --view-html https://x.com/ARCRaidersGame/status/2054607629738037736

# Simplify further with an alias
alias tw-article='article2img --guest --view-html'
tw-article https://x.com/XDevelopers/status/2041295840325636551

Set article_viewer = firefox in tw2img.conf to control which browser opens the file.

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

tw2img-0.4.0.tar.gz (55.5 kB view details)

Uploaded Source

Built Distribution

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

tw2img-0.4.0-py3-none-any.whl (52.2 kB view details)

Uploaded Python 3

File details

Details for the file tw2img-0.4.0.tar.gz.

File metadata

  • Download URL: tw2img-0.4.0.tar.gz
  • Upload date:
  • Size: 55.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.12

File hashes

Hashes for tw2img-0.4.0.tar.gz
Algorithm Hash digest
SHA256 d91c5661685be710dc203e493c863178ddfd0889672d54967a3609d1ab64dc16
MD5 9cee8a0aecc18fcfb43632c70f28531e
BLAKE2b-256 e32d084a019425090603309e52a549134e3c736a70599818de597ccf79323d8a

See more details on using hashes here.

File details

Details for the file tw2img-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: tw2img-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 52.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.12

File hashes

Hashes for tw2img-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1723ce6ef4965c4e4fb9f3cc23262eecc5bc5001416e8794cf23d7543a8a803b
MD5 1b85433c0348337a31df707c4f8d22f4
BLAKE2b-256 ec8c62ebf315fe46e305e96abc7ccae483259c69c78c4679831fb0ad8bd0eb87

See more details on using hashes here.

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