npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

kyzz-canvas

v2.0.0

Published

High-performance, lightweight custom canvas rendering engine for Node.js, Bun, Bots, and Web

Readme

Creator: Kyuu
Channel: https://whatsapp.com/channel/0029VbDO8tI2phHLTSN2ed0U
Contact: https://wa.me/6283150850721



📑 Daftar Isi


⚡ Mengapa Kyzz Canvas?

| Parameter | Kyzz Canvas | node-canvas | @napi-rs/canvas | |---|---|---|---| | Paradigma API | Fluent Modern Custom API | Legacy ctx imperative | Legacy ctx imperative | | Boilerplate ctx | Nol (Tidak perlu ctx) | Wajib const ctx = ... | Wajib const ctx = ... | | Bundled Font Bawaan | Ya (DejaVu Sans bawaan) | Tidak (bergantung font OS) | Tidak (bergantung font OS) | | Google Fonts Otomatis | Ya (fonts.google('Inter')) | Tidak (harus unduh manual) | Tidak (harus unduh manual) | | Remote SVG / CDN | Ya (Auto-fetch + SSRF Guard) | Tidak | Terbatas | | Dukungan Bun | 100% Native & Cepat | Sering gagal build/segfault | Perlu build flag khusus | | Ketergantungan Wrapper | Engine independen | C++ Cairo / Pango wrapper | Skia wrapper |


📦 Instalasi

Install melalui package manager favorit Anda:

# npm
npm install kyzz-canvas

# Bun
bun add kyzz-canvas

# pnpm
pnpm add kyzz-canvas

# Yarn
yarn add kyzz-canvas

🚀 Quick Start

Hanya butuh 4 baris untuk merender kartu selamat datang berkualitas tinggi:

import { Canvas, fonts } from 'kyzz-canvas';

await fonts.google('Poppins');

const canvas = new Canvas(800, 400);

canvas.bg({
  type: 'linear',
  x1: 0,
  y1: 0,
  x2: 800,
  y2: 400,
  stops: [
    { offset: 0, color: '#0f172a' },
    { offset: 1, color: '#1e1b4b' }
  ]
});

canvas.box(40, 40, 720, 320, {
  radius: 20,
  fill: 'rgba(255, 255, 255, 0.05)',
  stroke: 'rgba(255, 255, 255, 0.1)',
  strokeWidth: 1
});

canvas.circle(120, 120, 40, { fill: '#6366f1' });

canvas.text('Selamat Datang!', {
  x: 180,
  y: 115,
  size: 36,
  font: 'Poppins',
  weight: 700,
  color: '#ffffff'
});

canvas.text('Rendering modern tanpa ctx yang membingungkan.', {
  x: 180,
  y: 155,
  size: 18,
  font: 'Poppins',
  weight: 400,
  color: '#94a3b8'
});

const pngBuffer = await canvas.render('png');
await canvas.save('./output.png');

🌐 HTML + CSS Rendering Engine

Kyzz Canvas dilengkapi dengan HTML + CSS Rendering Engine bawaan yang dirancang khusus untuk menghasilkan gambar berkualitas tinggi dari sintaks HTML dan CSS modern, tanpa menggunakan browser.

[!IMPORTANT] 100% Bebas Browser (Zero Chromium / Headless Browser): Kyzz Canvas TIDAK membutuhkan Chromium, Puppeteer, Playwright, Chrome, atau Firefox. Engine memproses HTML dan CSS melalui layout & paint pipeline custom kami sendiri, menghasilkan gambar dalam milidetik dengan penggunaan RAM minimal (~30 MB).


Arsitektur Rendering (Tanpa Browser / Chromium)

Pipeline rendering Kyzz Canvas memisahkan setiap tahap pemrosesan dengan arsitektur modular:

HTML Input
    │
    ▼
Custom HTML Parser ───► Tokenisasi Tag, Atribut, Void Elements, Entitas
    │
    ▼
DOM-like Tree
    │
    ├── CSS Parser ────► Aturan CSS, @font-face, @import, Inline Styles
    │       │
    │       ▼
    │   Style Resolver ─► Cascading, Specificity, CSS Variables, Inheritance
    │
    ▼
Layout Engine ─────────► Box Model, Block/Inline Flow, Flexbox, Grid, Table, Positioning
    │
    ▼
Paint Engine ──────────► Stacking Context, Gradients, Shadows, Borders, Radii, Transforms
    │
    ▼
Kyzz Native Renderer ──► Vector Rasterization (Resvg) + Text & OpenType Shaper
    │
    ▼
Image Encoder ─────────► PNG / JPEG / WebP / SVG Buffer

Quick Example: HTML + CSS → Image

import { HTML } from 'kyzz-canvas';

const image = await HTML.render(`
  <style>
    .card {
      width: 700px;
      padding: 32px;
      border-radius: 24px;
      background: linear-gradient(135deg, #7c3aed, #ec4899);
      box-shadow: 0 16px 32px rgba(124, 58, 237, 0.35);
      color: white;
      font-family: Inter, sans-serif;
    }

    .title {
      font-size: 42px;
      font-weight: 700;
      margin-bottom: 8px;
    }

    .desc {
      font-size: 18px;
      opacity: 0.9;
    }
  </style>

  <div class="card">
    <div class="title">Kyzz Canvas</div>
    <p class="desc">Pure HTML + CSS → Image Rendering Engine</p>
  </div>
`, {
  width: 700,
  height: 'auto',
  deviceScaleFactor: 2 // Output tajam 1400px (retina)
});

// Simpan ke file
await image.save('card.png');

Atau gunakan method berantai langsung pada instance Canvas:

import { Canvas } from 'kyzz-canvas';

const canvas = new Canvas(800, 500);

await canvas.html(`
  <div style="width: 700px; padding: 24px; background: #1e1b4b; border-radius: 16px; color: white;">
    <h1>Kyzz Canvas</h1>
    <p>Chained canvas.html()</p>
  </div>
`, { x: 50, y: 50, width: 700 });

const buffer = await canvas.render('png');

Integrasi Bot (WhatsApp, Telegram, Discord)

Hasil dari HTML.render() dapat langsung di-pass sebagai Buffer gambar ke berbagai library bot populer:

WhatsApp Bot (Baileys)

import { HTML } from 'kyzz-canvas';

const buffer = await HTML.render(welcomeTemplate, {
  width: 800,
  height: 'auto',
  deviceScaleFactor: 2,
  variables: {
    username: 'Kyuu',
    memberCount: 1500
  }
});

await sock.sendMessage(remoteJid, {
  image: buffer,
  caption: 'Welcome to the server!'
});

Telegram Bot (Telegraf / Grammy)

const photo = await HTML.render(rankCardTemplate, {
  width: 720,
  height: 'auto',
  deviceScaleFactor: 2
});

await bot.sendPhoto(chatId, photo);

Discord Bot (discord.js)

const attachment = await HTML.render(leaderboardTemplate, {
  width: 800,
  height: 'auto'
});

await channel.send({
  files: [attachment]
});

Feature Matrix: Status Fitur Realistis

| Kategori | Fitur | Status | Detail & Catatan | |---|---|---|---| | HTML | Parser HTML | Stable | Parser pure-JS mandiri, mendukung tag modern, nested, entitas & custom tag | | HTML | Custom Elements | Stable | Mendukung tag custom seperti <profile-card>, <rank-badge> tanpa error | | HTML | Keamanan Script | Safe / Disabled | Tag <script>, atribut on*, dan URL javascript: diabaikan/dibersihkan | | CSS | CSS Parser | Stable | Mendukung <style>, inline style="", @font-face, @import, shorthand | | CSS | Selector & Specificity | Stable | *, tag, .class, #id, A B, A > B, A + B, A ~ B, [attr], :nth-child | | CSS | CSS Variables | Stable | Mendukung :root { --var: ... } dan var(--var, fallback) dengan inheritance | | CSS | CSS Calc | Stable | Evaluasi matematis calc() dengan operator +, -, *, / | | Layout | Box Model | Stable | content-box, border-box, margin (termasuk auto), padding, border | | Layout | Flexbox | Stable | direction, wrap, justify-content, align-items, gap, grow, shrink, order | | Layout | CSS Grid | Stable | grid-template-columns: repeat(...), fr, px, %, gap, auto-placement | | Layout | Positioning | Stable | static, relative, absolute, fixed, top/right/bottom/left, inset, z-index | | Layout | Auto Height | Stable | height: 'auto' otomatis menghitung tinggi total layout konten secara presisi | | Layout | Tables & Lists | Stable | <table>, <tr>, <th>, <td>, border-collapse, <ul>, <ol>, <li> | | Desain | Gradients | Stable | linear-gradient(), radial-gradient() dengan sudut dan multi-stop | | Desain | Box Shadow | Stable | Drop shadow blur, spread, offset-x/y, multiple shadow layers | | Desain | Borders & Radius | Stable | Sudut membulat individual (border-top-left-radius, dll.), gaya solid/dashed/dotted | | Efek | Transforms | Stable | rotate(), scale(), translate(), skew(), matrix(), transform-origin | | Efek | Filters | Stable | blur(), brightness(), contrast(), grayscale(), saturate(), sepia() | | Efek | Pseudo Elements | Stable | ::before dan ::after dengan content: "..." dan absolute positioning | | Efek | Clipping & Overflow | Stable | overflow: hidden, clip-path: circle(), ellipse(), polygon() | | Media | Gambar (<img>) | Stable | Local, remote, CDN, data URI, object-fit: cover / contain / fill, border-radius clip | | Media | Inline SVG (<svg>) | Stable | Vektor SVG langsung di-render dalam layout dengan styling CSS | | Font | Remote Web Fonts | Stable | Resolusi otomatis @font-face dengan URL, Google Fonts, dan font fallback | | Security| SSRF Guard | Stable | Pemblokiran otomatis IP privat, metadata cloud, localhost, dan timeout limit | | Kompabilitas| Browser DOM Penuh | Partial | Didesain khusus untuk layout & pembuatan gambar statis server-side | | Kompabilitas| Eksekusi JavaScript | Not Supported | Engine ini adalah static image renderer, bukan browser runtime |


Flexbox & CSS Grid Layout

Flexbox

Mendukung penuh tata letak kartu yang fleksibel:

<div class="row">
  <img class="avatar" src="avatar.png" />
  <div class="content">
    <h3>Username</h3>
    <p>Online</p>
  </div>
</div>

<style>
  .row {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 20px;
    background: #18181b;
  }
  .avatar {
    width: 64px;
    height: 64px;
    border-radius: 50%;
    object-fit: cover;
  }
  .content {
    flex: 1;
  }
</style>

CSS Grid

Mendukung pembuatan leaderboard, dashboard stats, dan galeri:

<div class="stats-grid">
  <div class="stat-box">Level: 45</div>
  <div class="stat-box">Wins: 120</div>
  <div class="stat-box">Rank: #3</div>
</div>

<style>
  .stats-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 12px;
    width: 600px;
  }
  .stat-box {
    background: #27272a;
    padding: 16px;
    border-radius: 12px;
    text-align: center;
  }
</style>

Template Variables & Custom Components

Variabel Template (variables)

Sisipkan variabel dinamis tanpa library templating eksternal:

await HTML.render(`
  <h1>Halo, {{user.name}}!</h1>
  <p>Poin Anda: {{points}}</p>
`, {
  variables: {
    user: { name: 'Kyuu' },
    points: 12500
  }
});

Custom Component

Daftarkan komponen reusable untuk kemudahan generator template:

HTML.component('rank-badge', ({ props }) => `
  <span style="background: #f59e0b; color: #000; font-weight: bold; padding: 4px 10px; border-radius: 8px;">
    RANK #${props.number || '1'}
  </span>
`);

await HTML.render(`
  <div class="card">
    <rank-badge number="1" />
  </div>
`);

Auto Height, Device Scale Factor & Measurement

Auto Height (height: 'auto')

Sangat berguna untuk kartu teks dinamis seperti quote card, log chat, atau leaderboard yang panjangnya berubah-ubah:

const result = await HTML.render(html, {
  width: 800,
  height: 'auto' // Tinggi dihitung otomatis berdasarkan konten
});

Measurement API (HTML.measure)

Menghitung dimensi tanpa merender atau mengkode gambar, menghemat CPU untuk layout planning:

const { width, height } = await HTML.measure(html, { width: 800 });
console.log(`Dimensi yang dibutuhkan: ${width}x${height}px`);

Device Scale Factor (Retina / 2x / 3x)

Hasilkan gambar beresolusi tajam tanpa mengubah ukuran CSS:

const retinaImage = await HTML.render(html, {
  width: 600,
  height: 400,
  deviceScaleFactor: 2 // Menghasilkan gambar 1200x800px super tajam
});

Debug Mode

Ingin melihat bagaimana parser memproses dokumen dan layout tree? Aktifkan debug: true:

const result = await HTML.render(html, {
  width: 800,
  debug: true
});

console.log(result.debugInfo.dom);    // Struktur pohon DOM
console.log(result.debugInfo.layout); // Koordinat setiap layout box
console.log(result.debugInfo.styles); // Computed CSS style setiap element

🏛️ Arsitektur & Konsep Engine

Kyzz Canvas dirancang dari awal dengan pemisahan komponen yang bersih:

Kyzz Canvas
├── Canvas                 High-level fluent builder & orchestration
├── Scene Graph            Hierarchical vector tree & transformation stack
├── Font Manager           Multi-source loader, shaper & fallback resolver
├── Universal Asset Loader HTTP/HTTPS, GitHub, CDN, Cloudflare, Buffer, Data URL
├── Security Guard         SSRF protector, private IP blocker & SVG sanitizer
├── Image Engine           Sharp-accelerated resizing, rounded avatar, blur
├── SVG Engine             Vector parser, path normalizer & viewBox scaler
└── Encoders               libvips-powered PNG, JPEG, WebP, GIF generator

🌐 Universal Asset Loader

Kyzz Canvas memiliki sistem pemuatan aset universal terpadu dengan perlindungan keamanan bawaan:

Sumber Aset yang Didukung

  • Local Filesystem: Path relatif (./assets/logo.png) dan absolut (/var/data/avatar.jpg).
  • HTTP / HTTPS URL: Mengunduh aset jarak jauh secara otomatis.
  • GitHub URL: Otomatis mengubah URL github.com/.../blob/... menjadi URL raw tanpa error 404 HTML.
  • Google Fonts: Resolusi stylesheet dan unduhan binary font otomatis.
  • CDN: jsDelivr, unpkg, cdnjs, Cloudflare R2, Cloudflare Pages.
  • Data URL: Skema data:image/png;base64,... atau data:font/ttf;base64,....
  • Binary Objek: Buffer, Uint8Array, ArrayBuffer.
  • Web API Objek: Web API Response dan Blob.

Proteksi Keamanan SSRF Bawaan

Engine otomatis memvalidasi setiap URL eksternal:

  • Memblokir private network & localhost (127.0.0.1, localhost, 10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12).
  • Memblokir cloud metadata endpoints (169.254.169.254).
  • Memblokir protokol non-HTTP seperti file://, ftp://, gopher://.
  • Mencegah serangan SVG XXE dan injeksi tag <script> / handler onload.

Konfigurasi Cache & Timeout

import { assets } from 'kyzz-canvas/assets';

assets.config({
  cache: true,
  maxItems: 200,
  maxSize: '150MB',
  timeout: 10000,
  diskCache: true,
  cacheDir: './.kyzz-cache'
});

🔤 Sistem Font & Google Fonts

1. Zero-Config Bundled Font

Setiap instalasi kyzz-canvas menyertakan font DejaVu Sans. Teks langsung dirender tanpa konfigurasi tambahan pada Docker container atau Linux serverless.

2. Mengambil Google Fonts

import { fonts } from 'kyzz-canvas/fonts';

await fonts.google('Inter');

await fonts.google('Fira Code', {
  weight: [400, 600, 700],
  style: 'normal'
});

3. Font dari GitHub & Remote CDN

await fonts.add('https://github.com/google/fonts/blob/main/ofl/roboto/Roboto-Bold.ttf', {
  family: 'Roboto',
  weight: 700
});

await fonts.add('https://cdn.jsdelivr.net/npm/@fontsource/inter/files/inter-latin-400-normal.woff');

4. Font dari Local File & Buffer

fonts.add('./fonts/CustomFont.ttf', { family: 'CustomFont' });

fonts.add(fontBuffer, { family: 'DynamicFont', weight: 600 });

5. Multi-Font Fallback Otomatis

fonts.fallback([
  'Inter',
  'Noto Sans Arabic',
  'Noto Sans JP',
  'Noto Color Emoji',
  'DejaVu Sans',
  'sans-serif'
]);

Jika font utama tidak memiliki glyph tertentu (misal: tulisan Arab, Kanji, atau Emoji), engine secara otomatis mencari glyph ke font fallback dalam urutan rantai yang ditentukan.


🖼️ Manipulasi Gambar & Foto Profil (PP)

Kyzz Canvas dirancang khusus untuk memproses foto profil bot WhatsApp, Discord, dan Telegram tanpa repot:

import { Canvas, Image, KyzzImage, createImage } from 'kyzz-canvas';

const img = await Image.from('https://example.com/avatar.jpg');

const roundAvatar = await img
  .resize(200, 200)
  .circle()
  .toBuffer();

const cardBanner = await Image.from('./assets/banner.png')
  .resize(800, 300)
  .roundCorners(24)
  .blur(2)
  .toBuffer('webp');

Menempelkan Foto Profil (PP) dengan Fallback Otomatis

Jika URL foto profil kosong (null, undefined, atau privacy WhatsApp diset ke private) atau gagal dimuat dari WhatsApp CDN, gunakan fitur fallback:

await canvas.image(ppUrl, {
  x: 40,
  y: 40,
  width: 120,
  height: 120,
  radius: 60,
  fit: 'cover',
  fallback: true // otomatis menampilkan avatar kontak bawaan jika link kosong atau gagal!
});

Anda juga dapat menyediakan file fallback kustom:

await canvas.image(ppUrl, {
  x: 40,
  y: 40,
  width: 120,
  height: 120,
  radius: 60,
  fallback: './assets/avatar_contact.png'
});

Validasi Gambar (validateImage & isValidImage)

Sebelum memproses gambar dari user atau URL eksternal, Anda dapat memvalidasinya secara instan:

import { validateImage, isValidImage } from 'kyzz-canvas';

const check = await validateImage(avatarUrlOrBuffer);
if (check.valid) {
  console.log('Format:', check.format, 'Ukuran:', check.width, 'x', check.height);
} else {
  console.log('Bukan gambar valid:', check.error);
}

if (await isValidImage(input)) {
  await canvas.image(input, { x: 50, y: 50, width: 100, height: 100 });
}

📐 Engine SVG Kelas Satu

Kyzz Canvas mendukung parsing dan rendering SVG secara native, baik string SVG maupun URL eksternal:

await canvas.svg('<svg viewBox="0 0 100 100"><circle cx="50" cy="50" r="40" fill="#38bdf8"/></svg>', {
  x: 50,
  y: 50,
  width: 80,
  height: 80
});

await canvas.svg('https://cdn.example.com/icons/shield.svg', {
  x: 150,
  y: 50,
  width: 80,
  height: 80
});

Standalone SVG Rendering:

import { renderSvgToBuffer } from 'kyzz-canvas/svg';

const buffer = await renderSvgToBuffer('<svg>...</svg>', {
  width: 1024,
  height: 1024,
  format: 'png'
});

🛠️ Daftar Lengkap Method Canvas

Inisialisasi

const canvas = new Canvas(width, height);

Background

canvas.bg('#111827');
canvas.background('rgb(17, 24, 39)');

canvas.bg({
  type: 'linear',
  x1: 0,
  y1: 0,
  x2: 600,
  y2: 0,
  stops: [
    { offset: 0, color: '#3b82f6' },
    { offset: 1, color: '#8b5cf6' }
  ]
});

Kotak & Persegi Panjang (box / rect)

canvas.box(x, y, width, height, {
  radius: 16,
  fill: '#1e293b',
  stroke: '#38bdf8',
  strokeWidth: 2,
  shadow: {
    color: 'rgba(0, 0, 0, 0.4)',
    blur: 10,
    offsetX: 0,
    offsetY: 4
  }
});

Lingkaran & Elips (circle / ellipse)

canvas.circle(cx, cy, radius, {
  fill: '#10b981',
  stroke: '#ffffff',
  strokeWidth: 2
});

canvas.ellipse(cx, cy, rx, ry, {
  fill: '#ec4899'
});

Garis & Poligon (line / polygon / path)

canvas.line(x1, y1, x2, y2, {
  stroke: '#f59e0b',
  strokeWidth: 3,
  dash: [8, 4]
});

canvas.polygon([
  { x: 100, y: 50 },
  { x: 150, y: 150 },
  { x: 50, y: 150 }
], {
  fill: '#8b5cf6'
});

canvas.path('M 10 80 Q 95 10 180 80 T 350 80', {
  stroke: '#06b6d4',
  strokeWidth: 4,
  fill: 'none'
});

Tipografi & Teks (text)

canvas.text('Hello World', {
  x: 50,
  y: 100,
  size: 32,
  font: 'Poppins',
  weight: 600,
  color: '#ffffff',
  align: 'left',
  baseline: 'alphabetic',
  letterSpacing: 1.5,
  maxWidth: 400
});

Transformasi & State

canvas.save();
canvas.translate(100, 50);
canvas.rotate(45, cx, cy);
canvas.scale(1.5, 1.5);
canvas.restore();

Resource Management

canvas.dispose();

💾 Format Output & File Export

const pngBuf = await canvas.render('png');

const jpgBuf = await canvas.render('jpeg', { quality: 85 });

const webpBuf = await canvas.render('webp', { quality: 90, lossless: false });

const gifBuf = await canvas.render('gif');

await canvas.save('./card.png');
await canvas.save('./card.webp', { quality: 85 });

🤖 Contoh Implementasi Bot

WhatsApp Bot (Baileys)

import makeWASocket from '@whiskeysockets/baileys';
import { Canvas } from 'kyzz-canvas';

async function sendWelcome(sock, jid, pushName) {
  const canvas = new Canvas(800, 350);
  canvas.bg('#111827');
  canvas.box(30, 30, 740, 290, { radius: 16, fill: '#1f2937' });
  canvas.circle(120, 175, 50, { fill: '#25d366' });
  canvas.text(pushName, { x: 200, y: 160, size: 36, color: '#ffffff' });
  canvas.text('Selamat bergabung di grup!', { x: 200, y: 200, size: 20, color: '#9ca3af' });

  const buffer = await canvas.render('jpeg', { quality: 85 });

  await sock.sendMessage(jid, {
    image: buffer,
    caption: `Halo @${jid.split('@')[0]}! Selamat datang!`,
    mentions: [jid]
  });
}

Telegram Bot (Telegraf)

import { Telegraf } from 'telegraf';
import { Canvas } from 'kyzz-canvas';

const bot = new Telegraf(process.env.BOT_TOKEN);

bot.command('profile', async (ctx) => {
  const canvas = new Canvas(600, 300);
  canvas.bg('#0f172a');
  canvas.box(20, 20, 560, 260, { radius: 16, fill: '#1e293b' });
  canvas.text(ctx.from.first_name, { x: 50, y: 100, size: 32, color: '#38bdf8' });
  canvas.text(`ID: ${ctx.from.id}`, { x: 50, y: 140, size: 18, color: '#94a3b8' });

  const buffer = await canvas.render('png');
  await ctx.replyWithPhoto({ source: buffer });
});

bot.launch();

Discord Bot (discord.js)

import { Client, GatewayIntentBits, AttachmentBuilder } from 'discord.js';
import { Canvas } from 'kyzz-canvas';

const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers] });

client.on('guildMemberAdd', async (member) => {
  const channel = member.guild.systemChannel;
  if (!channel) return;

  const canvas = new Canvas(800, 300);
  canvas.bg('#18191c');
  canvas.box(20, 20, 760, 260, { radius: 20, fill: '#2f3136' });

  const avatarUrl = member.user.displayAvatarURL({ extension: 'png', size: 256 });
  await canvas.image(avatarUrl, { x: 50, y: 60, width: 140, height: 140, radius: 70 });

  canvas.text(member.user.tag, { x: 220, y: 130, size: 36, color: '#ffffff' });
  canvas.text(`Member #${member.guild.memberCount}`, { x: 220, y: 175, size: 20, color: '#5865f2' });

  const buffer = await canvas.render('png');
  const attachment = new AttachmentBuilder(buffer, { name: 'welcome.png' });

  await channel.send({ content: `Selamat datang, ${member}!`, files: [attachment] });
});

client.login(process.env.DISCORD_TOKEN);

🌐 Contoh Web Framework & Server

Express.js

import express from 'express';
import { Canvas } from 'kyzz-canvas';

const app = express();

app.get('/api/card', async (req, res) => {
  const title = req.query.title || 'Dynamic Card';

  const canvas = new Canvas(600, 300);
  canvas.bg('#0f172a');
  canvas.text(String(title), { x: 50, y: 150, size: 32, color: '#ffffff' });

  const image = await canvas.render('webp', { quality: 80 });

  res.setHeader('Content-Type', 'image/webp');
  res.setHeader('Cache-Control', 'public, max-age=3600');
  res.send(image);
});

app.listen(3000);

Hono

import { Hono } from 'hono';
import { Canvas } from 'kyzz-canvas';

const app = new Hono();

app.get('/og-image', async (c) => {
  const canvas = new Canvas(1200, 630);
  canvas.bg('#09090b');
  canvas.text('Hono + Kyzz Canvas', { x: 80, y: 320, size: 56, color: '#fafafa' });

  const buffer = await canvas.render('png');

  return c.body(buffer, 200, {
    'Content-Type': 'image/png',
    'Cache-Control': 'public, max-age=86400'
  });
});

export default app;

Fastify

import Fastify from 'fastify';
import { Canvas } from 'kyzz-canvas';

const fastify = Fastify();

fastify.get('/badge', async (request, reply) => {
  const canvas = new Canvas(300, 100);
  canvas.bg('#10b981');
  canvas.text('Status: Active', { x: 30, y: 60, size: 24, color: '#ffffff' });

  const buffer = await canvas.render('png');
  reply.type('image/png').send(buffer);
});

fastify.listen({ port: 3000 });

Bun Native HTTP

import { Canvas } from 'kyzz-canvas';

Bun.serve({
  port: 3000,
  async fetch(req) {
    const canvas = new Canvas(500, 200);
    canvas.bg('#0284c7');
    canvas.text('Powered by Bun & Kyzz', { x: 40, y: 110, size: 28, color: '#ffffff' });

    const buffer = await canvas.render('png');

    return new Response(buffer, {
      headers: { 'Content-Type': 'image/png' }
    });
  }
});

⚛️ Virtual Component / JSX Adapter

Anda dapat menggunakan objek bergaya JSX atau deklaratif untuk mendeskripsikan elemen:

import { renderElement } from 'kyzz-canvas/react';

const buffer = await renderElement({
  type: 'box',
  props: {
    width: 600,
    height: 300,
    bg: '#0f172a',
    children: [
      {
        type: 'text',
        props: {
          x: 40,
          y: 80,
          text: 'Declarative Canvas',
          size: 32,
          color: '#38bdf8'
        }
      }
    ]
  }
}, { format: 'png' });

🎨 HTML5 Canvas 2D Compatibility Adapter

Jika Anda memiliki kode lama yang menggunakan pola ctx.fillRect() atau ctx.fillText(), Anda dapat menggunakan adapter WebCanvas:

import { WebCanvas } from 'kyzz-canvas/web';

const webCanvas = new WebCanvas(500, 300);
const ctx = webCanvas.getContext('2d');

ctx.fillStyle = '#1e1b4b';
ctx.fillRect(0, 0, 500, 300);

ctx.fillStyle = '#a855f7';
ctx.font = '24px sans-serif';
ctx.fillText('HTML5 Canvas 2D Compatibility', 30, 80);

const buffer = await webCanvas.toBuffer('png');

📊 Tabel Benchmark Resmi

Pengujian dilakukan pada Node.js v22 & Bun v1.4 (AMD64 Linux):

| Kategori Pengujian | Latensi Rata-Rata | Throughput Render | |---|---|---| | Shapes & Vector Rendering | 42.35 ms | 1,417 /menit | | Typography & Text Metrics | 46.06 ms | 1,303 /menit | | Standalone SVG Rendering | 31.97 ms | 1,877 /menit | | 800x400 PNG Encoding | 0.96 ms | 62,271 /menit | | 800x400 JPEG Encoding | 1.81 ms | 33,138 /menit | | 800x400 WebP Encoding | 1.86 ms | 32,178 /menit | | Peak Heap Memory Usage | ~32.9 MB | Sangat hemat memori |


📦 Daftar Subpath Modules

Package diekspor secara modular untuk efisiensi treeshaking:

| Subpath Import | Isi & Fungsi | |---|---| | kyzz-canvas | Canvas, fonts, assets, KyzzImage, singleton utama | | kyzz-canvas/fonts | FontManager, fonts, Google Fonts resolver, OpenType shaper | | kyzz-canvas/svg | renderSvgToBuffer, parseSvgViewBox, engine SVG | | kyzz-canvas/image | KyzzImage, resize, crop, avatar circle, filter blur | | kyzz-canvas/assets | UniversalAssetLoader, assets, disk cache, SSRF validator | | kyzz-canvas/web | WebCanvas, CanvasRenderingContext2D adapter | | kyzz-canvas/react | renderElement, declarative virtual JSX adapter | | kyzz-canvas/html | HTML, HTMLEngine, parseHTML, parseStylesheet, full HTML+CSS renderer | | kyzz-canvas/errors | Kelas error terstruktur (HTMLRenderError, FontNotFoundError, KyzzCanvasError, dll.) |


🤖 Prompt AI / System Instructions for LLMs

Salin dan gunakan instruksi di bawah ini saat meminta bantuan AI (seperti ChatGPT, Claude, Gemini, Cursor, Copilot, atau Antigravity) untuk membuat kode menggunakan Kyzz Canvas:

Anda adalah asisten coding ahli dalam menggunakan library rendering gambar Node.js/Bun bernama "Kyzz Canvas" (package npm: "kyzz-canvas").

ATURAN PALING KRUSIAL:
1. JANGAN PERNAH menyarankan atau menggunakan "@napi-rs/canvas" atau "node-canvas".
2. JANGAN PERNAH menulis pola legacy "ctx" (seperti `canvas.getContext('2d')`, `ctx.fillText`, `ctx.fillRect`, `ctx.beginPath()`) kecuali user secara eksplisit meminta adapter "kyzz-canvas/web".
3. Kyzz Canvas memiliki API custom sendiri yang berorientasi objek, berantai (fluent), dan intuitif:
   - Membuat canvas: `const canvas = new Canvas(width, height)`
   - Background: `canvas.bg('#111')` atau `canvas.background({ type: 'linear', ... })`
   - Teks: `canvas.text('Hello', { x: 40, y: 80, size: 32, font: 'Inter', color: '#fff' })`
   - Kotak / Rect: `canvas.box(x, y, w, h, { radius: 16, fill: '#5865f2' })` atau `canvas.rect(...)`
   - Lingkaran: `canvas.circle(cx, cy, radius, { fill: '#10b981' })`
   - Elips: `canvas.ellipse(cx, cy, rx, ry, { fill: '#ec4899' })`
   - Garis: `canvas.line(x1, y1, x2, y2, { stroke: '#fff', strokeWidth: 2 })`
   - Poligon: `canvas.polygon([{x, y}, ...], { fill: '#fff' })`
   - Path SVG: `canvas.path('M ... Z', { fill: '#fff' })`
   - Gambar: `await canvas.image(urlOrBuffer, { x, y, width, height, radius })`
   - SVG: `await canvas.svg(svgStringOrUrl, { x, y, width, height })`
   - Render output: `const buffer = await canvas.render('png')` (bisa juga 'jpeg', 'webp', 'gif')
   - Simpan ke file: `await canvas.save('./output.png')`
   - Free memory: `canvas.dispose()`

PENGELOLAAN FONT:
- Library sudah memiliki font bawaan "DejaVu Sans" yang langsung aktif tanpa konfigurasi.
- Mengambil font Google secara langsung:
  `import { fonts } from 'kyzz-canvas/fonts';`
  `await fonts.google('Poppins', { weight: [400, 700] });`
- Font dari URL / GitHub / CDN:
  `await fonts.add('https://example.com/font.ttf', { family: 'MyFont' });`
- Rantai fallback otomatis:
  `fonts.fallback(['Poppins', 'DejaVu Sans', 'sans-serif']);`

MANIPULASI GAMBAR & AVATAR:
- `import { Image, KyzzImage } from 'kyzz-canvas';`
- `const avatar = await Image.from(urlOrBuffer).resize(200, 200).circle().toBuffer();`
- Menempel PP ke Canvas: `await canvas.image(ppUrl, { x: 40, y: 40, width: 120, height: 120, radius: 60, fallback: true });`
- Fallback otomatis menangani PP WhatsApp yang null/private atau koneksi lambat.

MODUL EKSPOR LANGSUNG DARI ROOT (PROFESSIONAL):
- Semua fitur dapat diimpor langsung dari root package:
  `import { Canvas, fonts, assets, Image, KyzzImage, WebCanvas, createCanvas, createImage, renderSvg } from 'kyzz-canvas';`
- Subpath ekspor opsional tetap tersedia (`kyzz-canvas/fonts`, `kyzz-canvas/image`, dll.).

KARAKTERISTIK TEKNIS:
- Multi-runtime: Kompatibel 100% dengan Node.js (CommonJS & ESM) dan Bun.
- Otomatis mendeteksi format gambar (JPEG, PNG, WebP) via magic bytes sehingga foto profil WhatsApp JPEG dirender sempurna tanpa blank.
- Selalu gunakan async/await saat memanggil `canvas.render()`, `canvas.save()`, `canvas.image()`, `canvas.svg()`, dan `fonts.add()`.

📄 Lisensi

Didistribusikan di bawah lisensi MIT. Bebas digunakan untuk keperluan komersial, open source, bot WhatsApp, Telegram, Discord, maupun project pribadi.

Dibuat dengan ❤️ oleh Kyzz Canvas Team & kyzzknz.