Package Exports
- @deathnaitsa/nishi-canvas
- @deathnaitsa/nishi-canvas/core
- @deathnaitsa/nishi-canvas/generators/bubble
- @deathnaitsa/nishi-canvas/generators/leaderboard
- @deathnaitsa/nishi-canvas/generators/level
- @deathnaitsa/nishi-canvas/generators/profile
- @deathnaitsa/nishi-canvas/generators/welcome
- @deathnaitsa/nishi-canvas/themes
Readme
@deathnaitsa/nishi-canvas
Cross-platform Canvas Adapter + Image Generator Kit
Runs on Windows, Linux, macOS, WSL, Termux and Docker — zero node-gyp, zero config.
Drop-in replacement for @napi-rs/canvas with extra generators and utilities built-in.
Installation
npm install @deathnaitsa/nishi-canvas@napi-rs/canvas ships as a bundled dependency with pre-built binaries for every platform — no compiling, no build tools required.
Want a different engine? Just install it alongside — the adapter picks it up automatically:
npm install skia-canvas # higher quality Skia renderer
npm install canvas # classic node-canvas (needs build tools)Engine priority: @napi-rs/canvas → skia-canvas → canvas
Quick Start
import Canvas from '@deathnaitsa/nishi-canvas';
const canvas = await Canvas.createCanvas(700, 250);
const ctx = canvas.getContext('2d');
// Extend ctx with extra utility methods
Canvas.extendContext(ctx, canvas);
ctx.fillStyle = '#5865F2';
ctx.fillRoundRect(10, 10, 680, 230, 16); // built-in polyfill
ctx.setFont(32, 'sans-serif', 'bold'); // size, family, weight in one call
ctx.fillStyle = '#ffffff';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('Hello!', 350, 125);
// Export as PNG Buffer
const png = await Canvas.toPNG(canvas);Generators
One function call returns a PNG Buffer — no setup, no boilerplate.
Welcome Card
import { generateWelcome } from '@deathnaitsa/nishi-canvas';
const png = await generateWelcome({
avatar: 'https://cdn.discordapp.com/avatars/...',
username: 'Seblo',
serverName: 'Nishi Server',
memberCount: 1337,
theme: 'dark', // 'dark' | 'light' | 'neon' | 'midnight'
accentColor: '#5865F2', // optional override
welcomeText: 'WELCOME', // optional header label
background: 'https://...', // image URL, hex color, or ['#color1','#color2'] gradient
});Level / Rank Card
import { generateLevel } from '@deathnaitsa/nishi-canvas';
const png = await generateLevel({
avatar: 'https://...',
username: 'Seblo',
level: 42,
xp: 1500,
maxXp: 3000,
rank: 5, // optional rank badge
theme: 'midnight',
});Profile Card
import { generateProfile } from '@deathnaitsa/nishi-canvas';
const png = await generateProfile({
avatar: 'https://...',
banner: 'https://...', // or '#hex' color
username: 'Seblo',
tag: '#0001',
bio: 'Building cool stuff.',
stats: { Level: 42, XP: '15.2k', Rank: '#5', Messages: 2847 },
badges: ['Admin', 'Premium', 'Early Bird'],
theme: 'neon',
});Speech Bubble
import { generateBubble } from '@deathnaitsa/nishi-canvas';
const png = await generateBubble({
text: 'Yo, was geht?',
avatar: 'https://...', // optional avatar next to bubble
tailDir: 'left', // 'left' | 'right' | 'top' | 'bottom'
theme: 'dark',
});Leaderboard
import { generateLeaderboard } from '@deathnaitsa/nishi-canvas';
const png = await generateLeaderboard({
title: 'Top Players',
entries: [
{ username: 'Seblo', value: 52000, label: 'XP', avatar: 'https://...' },
{ username: 'Anna', value: 41500, label: 'XP' },
{ username: 'Max', value: 37200, label: 'XP' },
],
theme: 'dark',
maxEntries: 10,
width: 500,
});Themes
| Name | Description |
|---|---|
dark |
Dark navy (default) |
light |
Clean white |
neon |
Black + neon green |
midnight |
Deep purple |
import { registerTheme } from '@deathnaitsa/nishi-canvas';
// Register a custom theme (missing keys fall back to dark)
registerTheme('sakura', {
accent: '#ff69b4',
background: '#fff0f5',
text: '#2c2f33',
});
const png = await generateWelcome({ username: 'Test', theme: 'sakura' });
// Or pass a plain object directly — no registration needed
const png2 = await generateLevel({ username: 'Test', theme: { accent: '#ff0000' } });Extended Context API
Call Canvas.extendContext(ctx, canvas) once to get these extra methods:
| Method | Description |
|---|---|
ctx.fillRoundRect(x, y, w, h, r) |
Fill rounded rectangle |
ctx.strokeRoundRect(x, y, w, h, r) |
Stroke rounded rectangle |
ctx.circle(x, y, r, fill?, stroke?) |
Draw full circle |
ctx.avatarCircle(img, cx, cy, r) |
Image clipped to circle |
ctx.roundedImage(img, x, y, w, h, r) |
Image clipped to rounded rect |
ctx.setFont(size, family, weight, style) |
Set font in one call |
ctx.centeredText(text, x, y, boundWidth) |
Horizontally centered text |
ctx.multilineText(text, x, y, maxW, lineH) |
Word-wrapped text |
ctx.truncateText(text, maxWidth) |
Truncate with … |
ctx.progressBar(x, y, w, h, progress, fill, bg) |
Progress bar |
ctx.linearGradient(stops, x1, y1, x2, y2) |
Linear gradient from array |
ctx.shadow(color, blur, offsetX, offsetY) |
Set shadow in one call |
ctx.clearShadow() |
Remove all shadows |
ctx.with(fn) |
save() → fn → restore() |
ctx.clear() |
Clear entire canvas |
Color Utilities
import { alpha, darken, lighten, mix, contrastColor } from '@deathnaitsa/nishi-canvas';
alpha('#5865F2', 0.5) // → 'rgba(88, 101, 242, 0.5)'
darken('#5865F2', 20) // → darker hex
lighten('#5865F2', 20) // → lighter hex
mix('#ff0000', '#0000ff', 0.5) // → mixed color
contrastColor('#5865F2') // → '#ffffff' or '#000000' for readable contrastImage Loading
Handles any source type automatically:
import { loadImageFromAny, tryLoadImage } from '@deathnaitsa/nishi-canvas';
const img = await loadImageFromAny('https://example.com/avatar.png'); // URL
const img2 = await loadImageFromAny('./local/image.png'); // local path
const img3 = await loadImageFromAny(someBuffer); // Buffer
const img4 = await loadImageFromAny('data:image/png;base64,...'); // Base64
// Returns null on failure instead of throwing
const img5 = await tryLoadImage(maybeInvalidUrl);Font Management
import { loadBuiltinFonts, registerFontFile, loadFontsFromDir } from '@deathnaitsa/nishi-canvas';
// Auto-load all fonts from the fonts/ directory
await loadBuiltinFonts();
// Register a single font manually
await registerFontFile('./fonts/Roboto-Bold.ttf', 'Roboto', { weight: 'bold' });
// Load all fonts from a directory
await loadFontsFromDir('./my-fonts/');Project Structure
@deathnaitsa/nishi-canvas
├── dist/ ← Obfuscated build (ESM + CJS)
│ ├── index.js
│ ├── index.cjs
│ ├── core/
│ ├── generators/
│ ├── themes/
│ └── utils/
└── fonts/ ← Drop .ttf/.otf files hereLicense
MIT © DeathNaitsa