How to Build a Chat Screenshot Generator with Node.js and Canvas

This guide walks you through creating a Node.js tool that automatically generates chat screenshots with customizable themes, avatars, and text, complete with code snippets, size calculations, drawing functions, and integration into workflow tools like Alfred and macOS Shortcuts for instant clipboard copying.

BaiPing Technology
BaiPing Technology
BaiPing Technology
How to Build a Chat Screenshot Generator with Node.js and Canvas

Why Build This Tool

Inspired by the inconvenience of manually typing and screenshotting chat messages, the author created a "Redundant Generator" to automatically produce chat screenshots from input text.

Generate Screenshot

Initialize Project

We use Node.js to develop the tool. First, create a working directory and initialize an npm project.

mkdir redundant-tools && cd redundant-tools
npm init

Install the canvas package so Node can draw graphics. npm i canvas Note: The canvas package may require a proper gcc environment matching the local Node version; refer to the official documentation if installation fails.

Analyze Screenshot Composition

A screenshot consists of a background, avatar, bubble, and chat text. The background and bubble colors change based on the selected theme (dark/light).

Calculate Height

The final height equals the vertical padding (20 px × 2) plus bubble padding (17 px × 2) plus the number of lines multiplied by the line height (42 px).

const chatLineHeight = 42;
const globalPadding = 20;
const bubblePadding = [17, 26, 24]; // top, right, left
const canvasHeight = lines.length * chatLineHeight + globalPadding * 2 + bubblePadding[0] * 2;

Calculate Width

The final width equals the horizontal padding (20 px × 2) plus the text width plus bubble left/right padding, avatar size, and bubble‑avatar margin.

const bubbleRightMargin = 10;
const headSize = 64;
const canvasWidth = Math.ceil(lineWidth) + globalPadding * 2 + paddingLeft + paddingRight + bubbleRightMargin + headSize;

Background

Draw a rectangle the same size as the canvas and fill it with the theme background color.

ctx.fillStyle = bgColor;
ctx.fillRect(0, 0, canvasWidth, canvasHeight);

Bubble

Instead of using a stretchable .9.png, the bubble is drawn as a rounded rectangle with an arrow image.

// Draw bubble background
ctx.save();
ctx.translate(globalPadding, globalPadding);
drawRoundRectPath(ctx, bubbleWidth, bubbleHeight, 8);
ctx.clip();
ctx.fillStyle = bubbleBgColor;
ctx.fillRect(0, 0, bubbleWidth, bubbleHeight);
ctx.restore();

Load the arrow image (base64) and draw it at the calculated position.

const arrowImage = await loadImage(arrowBg);
ctx.drawImage(arrowImage, 0, 0, 10, 16, arrowX, arrowY, 10, 16);

Avatar

The avatar URL is taken from an environment variable, loaded with loadImage, and drawn with rounded corners.

const headX = canvasWidth - globalPadding - headSize;
drawImageWidthRadios(ctx, headImage, headX, globalPadding, headSize, headSize, 4);

Chat Text

After wrapping the text into lines, each line is drawn inside the bubble.

function drawChatText(ctx, x, y, lines) {
  lines.forEach((line, index) => {
    const drawY = y + chatLineHeight * index;
    ctx.beginPath();
    ctx.font = '28px "PingFangSC-Regular", sans-serif, Arial, SF Pro Display';
    ctx.fillStyle = '#05110a';
    ctx.textBaseline = 'top';
    ctx.fillText(line, x, drawY);
    ctx.closePath();
  });
}

const textStartX = globalPadding + paddingLeft;
const textStartY = globalPadding + paddingTop;
drawChatText(ctx, textStartX, textStartY, lines);

Save File

Export the canvas to a PNG file using toBuffer and Node's fs module.

const fs = require('fs');
fs.writeFileSync(`${projectPath}/output.png`, canvas.toBuffer());

Convenient Usage

Run the tool from the project root with the avatar URL, project path, chat text, and theme:

headUrl=https://fmcat-images.oss-cn-hangzhou.aliyuncs.com/head.png projectPath=. node index.js "This is dark theme text" dark

To make the workflow smoother, the author integrates the tool with Alfred and macOS Shortcuts.

Alfred Workflow

Set up environment variables ( headUrl, nodePath, projectPath) and create keyword triggers ( dd for dark, dl for light). Use conditional blocks to ensure required variables are set, then run a script that executes the Node command and copies the generated image to the clipboard via AppleScript.

osascript -e 'set the clipboard to (read (POSIX file "{query}") as JPEG picture)'

Notify the user when the image is ready.

macOS Shortcuts

Configure a shortcut that asks for the theme and chat text, runs the same Node script, and copies the resulting PNG to the clipboard using the same AppleScript command.

Conclusion

Although the "Redundant Generator" is a playful project, it demonstrates useful techniques: canvas drawing with Node.js, dynamic size calculation, and seamless integration with workflow tools to turn a multi‑step process into a single shortcut.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

AutomationNode.jsAlfredAppleScriptChat ScreenshotmacOS Shortcuts
BaiPing Technology
Written by

BaiPing Technology

Official account of the BaiPing app technology team. Dedicated to enhancing human productivity through technology. | DRINK FOR FUN!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.