βββΒ· β .β βββββ βββΒ· βββ βββΒ· βββββ
ββ ββ βββͺββ ββββ β’ββ ββ ββ βββ’ ββ ββ β’ββ
βββββ ββββββββ.ββ ββ.βͺ βββΒ· βββͺ βββΒ· ββ.βͺ
ββ βͺββββββββββ.ββ βββΒ·βββͺΒ·β’ ββββββββͺΒ·β’ βββΒ·
β β βββ Β· βββββͺ βββ .β .βββ .β βββHTML in, image out. One Go binary that drives a real headless Chromium, so modern CSS renders exactly the way Chrome shows it.
Web "HTML to image" tools mean uploading your markup to someone's server, then cropping the result by hand. shotpls does it locally: point it at an HTML file (or a whole folder), get pixel-exact PNGs back at whatever size and pixel density you want. It was built to turn standalone Instagram-carousel slide files into ready-to-post 2160x2700 images with zero cropping, but it renders any HTML.
- Chrome-accurate rendering β drives your installed Chrome/Chromium over the DevTools protocol. Flexbox, grid,
@font-face, SVG, blend modes, custom properties: whatever Chrome does, you get. - Exact geometry, no cropping β a fixed viewport plus a device scale factor.
1080x1350 @2xgives you a2160x2700PNG, edge to edge, every time. - Waits for fonts and images β blocks on
document.fonts.readyand image decode before the shot, so you never capture a half-loaded page.--delayadds settle time for JS-heavy pages. - One file or a whole folder β pass a directory and it renders every
.htmlinside, preserving theNN-ordering in the output names. - PNG or JPEG β
--format jpeg --quality 88when you want smaller files. - Single static binary, zero Go runtime dependencies. The only runtime requirement is a Chrome/Chromium on your machine.
A recent Chrome or Chromium on your PATH (google-chrome, chromium, or chromium-browser). Point at a specific binary with --chrome /path/to/chrome if it lives somewhere unusual.
curl -fsSL https://raw.githubusercontent.com/CarterPerez-dev/shotpls/main/install.sh | bashThe installer detects your OS / architecture, downloads the matching pre-built binary from the latest GitHub release (when available), falls back to go install if no binary exists for your platform, and adds ~/.shotpls/bin to your PATH for bash, zsh, or fish.
Override with environment variables:
SHOTPLS_INSTALL_DIR=$HOME/.local/bin \
SHOTPLS_VERSION=v0.1.0 \
bash <(curl -fsSL https://raw.githubusercontent.com/CarterPerez-dev/shotpls/main/install.sh)go install github.com/CarterPerez-dev/shotpls@latestgit clone https://github.com/CarterPerez-dev/shotpls.git
cd shotpls
go build -o shotpls .# One file -> slide.png (2160x2700) next to it
shotpls slide.html
# A whole folder -> PNGs in the current directory, NN- order preserved
shotpls htmls/ -o .
# JPEG instead of PNG
shotpls hero.html --format jpeg --quality 88
# A square 3x render
shotpls card.html --width 1080 --height 1080 --scale 3
# A JS-heavy page that needs a moment to settle
shotpls dashboard.html --delay 500| Flag | Description |
|---|---|
-o, --out DIR |
Output directory (default: same dir as each input) |
--width N |
Viewport width in CSS px (default: 1080) |
--height N |
Viewport height in CSS px (default: 1350) |
--scale N |
Device scale factor (default: 2, so 1080x1350 becomes 2160x2700) |
--format FMT |
png or jpeg (default: png) |
--quality N |
JPEG quality 1-100 (default: 92) |
--delay MS |
Extra settle time after load, in milliseconds (default: 0) |
--timeout SEC |
Per-file render timeout in seconds (default: 30) |
--chrome PATH |
Path to a specific Chrome/Chromium binary |
-h, --help |
Show help |
-v, --version |
Show version |
Flags work in any position β shotpls htmls/ --scale 2 is the same as shotpls --scale 2 htmls/.
Output pixel dimensions are simply width x scale by height x scale:
--width 1080 --height 1350 --scale 2 -> 2160 x 2700 (Instagram portrait 4:5 @2x)
--width 1080 --height 1080 --scale 2 -> 2160 x 2160 (square @2x)
--width 1200 --height 630 --scale 2 -> 2400 x 1260 (OG image @2x)
Under the hood shotpls sets a device-metrics override (viewport + device scale factor) on a headless Chromium tab, navigates to the file:// URL, waits for document.fonts.ready and every image to finish decoding, then captures a screenshot clipped to exactly 0,0 -> width,height. No page chrome, no scrollbars, no cropping.
For batch runs the browser is launched once and reused across every file in the folder.
main.go Entry point
internal/
app/app.go Flag parsing + orchestration
app/app_test.go Table-driven tests
render/render.go Headless-Chromium rendering (chromedp)
install.sh Curl-installable bootstrapper
Single external dependency: github.com/chromedp/chromedp for driving Chrome.
Requires Go 1.24+ (the build auto-fetches the Go 1.26 toolchain declared in go.mod) and a local Chrome/Chromium.
go test ./... # Run unit tests
go vet ./... # Static analysis
go build -o shotpls . # Build local binary
just shot slide.html # Render through local sourceAGPL 3.0