π¨ Discord HTML Canvas
π‘ The Problem
Creating rank cards, welcome images, or custom graphics for Discord bots currently requires:
const canvas = createCanvas(800, 400);
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#7289DA';
ctx.fillRect(0, 0, 800, 400);
ctx.font = 'bold 42px Arial';
ctx.fillStyle = '#FFFFFF';
ctx.fillText('Username', 120, 80);
ctx.beginPath();
ctx.arc(60, 60, 50, 0, Math.PI * 2);
β¨ The Solution
const html = `
<div style="width: 800px; height: 400px; background: #7289DA;
display: flex; align-items: center; padding: 20px;">
<img src="${avatar}" style="width: 100px; height: 100px; border-radius: 50%;" />
<h1 style="color: white; margin-left: 20px;">Username</h1>
</div>
`;
const buffer = await renderHtmlToBuffer(html);
Save hours of development time. Build what you imagine, not what coordinates allow.
π Features
- π¨ Design with HTML/CSS - Use the tools you already know
- π¦ Pre-built Templates - Rank cards, welcome images, level-up cards
- π Library Agnostic - Works with discord.js v14+ and Eris v0.17+
- β‘ Type-Safe - Full TypeScript support with comprehensive types
- π― Production Ready - Powered by Satori (by Vercel)
- πΌοΈ Image Optimization - Automatic PNG conversion with high quality
- π€ Custom Fonts - Load and use your own TTF/OTF/WOFF fonts
- π Modern CSS - Gradients, flexbox, shadows, and more
- π Lightweight - Minimal dependencies, maximum performance
- πΎ Zero Dependencies - discord.js and Eris are optional peer deps
π¦ Installation
npm install discord-html-canvas
Peer Dependencies
For Discord.js:
npm install discord.js@^14.0.0
For Eris:
npm install eris@^0.17.0
π― Quick Start
Simple Example
import { renderHtmlToBuffer } from 'discord-html-canvas';
const html = `
<div style="width: 800px; height: 400px; background: linear-gradient(to right, #667eea, #764ba2);
color: white; display: flex; flex-direction: column; justify-content: center;
align-items: center; font-family: Arial;">
<h1 style="font-size: 48px; margin: 0;">Welcome to the Server!</h1>
<p style="font-size: 24px; margin-top: 20px;">You are member #1234</p>
</div>
`;
const buffer = await renderHtmlToBuffer(html);
Using Pre-built Templates
import { createRankCard, renderHtmlToBuffer } from 'discord-html-canvas';
const html = createRankCard({
username: 'CoolUser',
discriminator: '0001',
avatar: 'https://cdn.discordapp.com/avatars/...',
level: 42,
currentXP: 7500,
requiredXP: 10000,
rank: 15,
accentColor: '#7289DA',
});
const buffer = await renderHtmlToBuffer(html);
With Discord.js
import { Client, AttachmentBuilder } from 'discord.js';
import { createWelcomeCard, renderHtmlToBuffer } from 'discord-html-canvas';
client.on('guildMemberAdd', async (member) => {
const html = createWelcomeCard({
username: member.user.username,
avatar: member.user.displayAvatarURL({ extension: 'png' }),
guildName: member.guild.name,
memberCount: member.guild.memberCount,
});
const buffer = await renderHtmlToBuffer(html);
const attachment = new AttachmentBuilder(buffer, { name: 'welcome.png' });
await member.guild.systemChannel?.send({
content: `Welcome ${member}!`,
files: [attachment],
});
});
π Pre-built Templates
Rank Card
Perfect for leveling systems and leaderboards.
import { createRankCard } from 'discord-html-canvas';
const html = createRankCard({
username: 'EliteGamer',
discriminator: '1337',
avatar: 'https://...',
level: 99,
currentXP: 15000,
requiredXP: 20000,
rank: 3,
backgroundColor: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
accentColor: '#FFD700',
});
Features:
- User avatar with styled border
- Username and discriminator
- Level and rank display
- Progress bar with XP stats
- Customizable colors and gradients
- Optional background image
Welcome Card
Beautiful welcome images for new members.
import { createWelcomeCard } from 'discord-html-canvas';
const html = createWelcomeCard({
username: 'NewMember',
avatar: 'https://...',
guildName: 'Awesome Server',
memberCount: 5000,
message: 'Welcome to our community!',
backgroundColor: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
});
Features:
- User avatar
- Custom welcome message
- Guild name and member count
- Customizable background
- Professional styling
Level Up Card
Celebrate level milestones.
import { createLevelUpCard } from 'discord-html-canvas';
const html = createLevelUpCard({
username: 'ProPlayer',
avatar: 'https://...',
oldLevel: 49,
newLevel: 50,
backgroundColor: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)',
accentColor: '#FFD700',
});
π¨ Advanced Usage
Custom Styling
Use modern CSS features:
const html = `
<div style="width: 1000px; height: 600px;
background: linear-gradient(135deg, #1e3c72 0%, #7e8ba3 100%);
border-radius: 20px;
padding: 40px;
display: flex;
flex-direction: column;
font-family: Arial;">
<div style="display: flex; gap: 20px;">
<div style="flex: 1;
background: rgba(255,255,255,0.1);
border-radius: 15px;
padding: 25px;
box-shadow: 0 8px 16px rgba(0,0,0,0.2);">
<h2 style="color: #FFD700; font-size: 24px;">Wins</h2>
<p style="color: white; font-size: 64px; font-weight: bold;">1,234</p>
</div>
<div style="flex: 1;
background: rgba(255,255,255,0.1);
border-radius: 15px;
padding: 25px;">
<h2 style="color: #FF6B6B; font-size: 24px;">Losses</h2>
<p style="color: white; font-size: 64px; font-weight: bold;">567</p>
</div>
</div>
</div>
`;
Custom Fonts
Load and use your own fonts:
import { HtmlCanvas } from 'discord-html-canvas';
import { readFileSync } from 'fs';
const canvas = new HtmlCanvas();
const fontData = readFileSync('./fonts/MyCustomFont.ttf');
await canvas.loadFont({
name: 'CustomFont',
data: fontData,
weight: 400,
style: 'normal',
});
const html = `
<div style="font-family: CustomFont; font-size: 48px; color: white;">
Using my custom font!
</div>
`;
const buffer = await canvas.render(html);
Using the Adapter Pattern
For easier integration with Discord libraries:
import { HtmlCanvas, DiscordJSAdapter } from 'discord-html-canvas';
import { Client } from 'discord.js';
const client = new Client({ ... });
const canvas = new HtmlCanvas();
const adapter = new DiscordJSAdapter(client);
await adapter.sendImageToChannel('channel-id', buffer, 'image.png');
await adapter.replyWithImage(message, buffer, 'reply.png');
await adapter.sendImageAsAttachment(interaction, buffer, 'response.png');
π API Reference
renderHtmlToBuffer(html, options?)
Simple function to convert HTML to PNG buffer.
Parameters:
html: string - HTML string to render
options?: RenderOptions - Optional rendering options
Returns: Promise<Buffer> - PNG image buffer
Example:
const buffer = await renderHtmlToBuffer(html, {
width: 800,
height: 400,
backgroundColor: '#ffffff',
});
class HtmlCanvas
Main rendering class with advanced options.
Constructor
new HtmlCanvas(defaultOptions?: RenderOptions)
Methods
render(html: string, options?: RenderOptions): Promise<Buffer | string> - Render HTML to image
setDefaultOptions(options: RenderOptions): void - Set default render options
loadFont(font: FontOptions): Promise<void> - Load and cache a font
clearFontCache(): void - Clear cached fonts
RenderOptions
interface RenderOptions {
width?: number;
height?: number;
fonts?: FontOptions[];
backgroundColor?: string;
format?: 'png' | 'svg';
}
FontOptions
interface FontOptions {
name: string;
data: ArrayBuffer | Buffer;
weight?: number;
style?: 'normal' | 'italic';
}
Template Functions
createRankCard(data: RankCardData): string
createWelcomeCard(data: WelcomeCardData): string
createLevelUpCard(data: LevelUpData): string
See TypeScript definitions for complete data interfaces.
π― Use Cases
Leveling Systems
- Rank cards showing XP and progress
- Level-up announcements
- Leaderboard graphics
Welcome Systems
- Custom welcome images
- Server rules cards
- Member milestone celebrations
Statistics
- Gaming stats cards
- Server analytics
- User profiles
Notifications
- Achievement unlocked
- Role assignments
- Event announcements
π Migration from Manual Canvas
Before (with @napi-rs/canvas):
const canvas = createCanvas(800, 400);
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#7289DA';
ctx.fillRect(0, 0, 800, 400);
const avatarImg = await loadImage(avatarURL);
ctx.save();
ctx.beginPath();
ctx.arc(100, 100, 50, 0, Math.PI * 2);
ctx.closePath();
ctx.clip();
ctx.drawImage(avatarImg, 50, 50, 100, 100);
ctx.restore();
ctx.font = 'bold 42px Arial';
ctx.fillStyle = '#FFFFFF';
ctx.fillText('Username', 170, 90);
const barWidth = 500;
const barHeight = 30;
const progress = (7500 / 10000) * barWidth;
ctx.fillStyle = '#2C2F33';
ctx.fillRect(50, 250, barWidth, barHeight);
ctx.fillStyle = '#3BA55C';
ctx.fillRect(50, 250, progress, barHeight);
After (with discord-html-canvas):
const html = createRankCard({
username: 'Username',
avatar: avatarURL,
level: 42,
currentXP: 7500,
requiredXP: 10000,
rank: 15,
});
const buffer = await renderHtmlToBuffer(html);
Result: Same output, 90% less code, infinitely more maintainable.
β‘ Performance
- Fast: Satori is optimized for server-side rendering
- Efficient: Cached fonts reduce repeated file reads
- Scalable: Handle concurrent requests easily
- Lightweight: Minimal bundle size
π€ Contributing
Contributions are welcome! Please check out the Contributing Guide.
π License
This project is licensed under the MIT License - see the LICENSE file for details.
π Acknowledgments
π Links
β Star us on GitHub if you find this helpful!
Stop calculating coordinates. Start creating beautiful images.