JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 43
  • Score
    100M100P100Q81163F
  • License MIT

Low-level JS library for generating beautiful .pptx files — pptxgenjs-compatible API with designer-grade primitives (global theme, parallel raster pipeline, LRU cache, addTable/Quote/Divider/Avatar, Lucide icons, Prism syntax highlighting, blobs, mesh gradients, glass-morphism, grain, gradient text, isometric grids, editorial charts, dot grids, progress rings, sparklines, badges, timelines, aurora gradients, halftones, radar charts, heatmaps, funnels, KPI cards, callouts, feature cards, step flows, comparison tables, team cards) + live SVG preview.

Package Exports

  • slidewave
  • slidewave/package.json

Readme

Slidewave

Low-level JS library for generating beautiful .pptx files. pptxgenjs-compatible API, but with designer-grade primitives that LLMs can't uniformize.

npm version license

What is Slidewave?

Slidewave is a JavaScript library that generates .pptx files by code — openable in PowerPoint, Keynote and Google Slides. It's a thin layer on top of pptxgenjs: same low-level API (inches, addText, addShape…) but with extra designer-grade primitives that are impossible to click in PowerPoint.

Why

  1. Programmatic generation — 100 slides in 1 second, dynamic templates, real-time data.
  2. Modern visuals — mesh gradients, aurora gradients, organic blobs, glassmorphism, halftones, editorial charts, syntax-highlighted code, Lucide icons.
  3. Still editable — text, shapes and charts remain native PPT objects, modifiable inside PowerPoint.
  4. Zero lock-in — standard .pptx output. Works everywhere.
  5. Live SVG preview — see the result in-browser without opening PowerPoint.
  6. LLM-friendly — Claude / GPT / Gemini emit editorial-grade slides instead of the usual rectangles.

Feature catalogue (v0.5)

Native primitives — editable in PowerPoint

Primitive What it does
addText Editable text (fontFace, charSpacing, shadow, glow, outline)
addRect, addShape, addLine Standard shapes + radius + border + shadow
addImage URL / base64 / dataURL
addChart Editorial-styled bar/line/pie/area/doughnut
addTable Native PPT table (4 presets: editorial, minimal, grid, striped)
addQuote Blockquote with 3 styles (classic, pullquote, minimal)
addDivider Section break (line, double, labeled, dots)
addAvatar Circular avatar (initials or image) + optional name/role
addBadge Pill (rounded rect + centered text)
addProgressBar Horizontal progress bar + % label
addTimeline Horizontal timeline with dot milestones
addConnector Arrow between two points (auto-oriented head)
addStatCard Large value + label + delta ▲▼ + optional sparkline
addKPIGrid Row of 2/3/4 stat cards
addCallout Colored callout box (info / success / warning / danger)
addFeatureCard Lucide icon + title + body card
addStepFlow Numbered steps 1→2→3… with connecting lines
addComparisonTable ✓/✗ feature comparison with highlighted column
addLogoCloud Auto-grid of logo images ("trusted by")
addTeamCard Avatar + name + role + bio

Raster primitives — SVG→PNG, pixel-perfect (await required)

Primitive What it does
addBackground Linear / radial / mesh gradient full-slide fill
addAuroraGradient Organic multi-blob aurora mesh gradient
addHalftone Dot halftone (horizontal / vertical / radial fade)
addGridPaper Fine grid with major lines (Notion / Figma style)
addCheckerPattern 2-color checkerboard
addRadialBurst Retro sun-burst rays from configurable center
addNoiseTexture Colored film grain via SVG feTurbulence
addGradientText Text with multi-stop gradient fill
addBlob Organic seeded blob shapes
addGlassCard Glassmorphism card (tint + border + highlight)
addGrain Cinema grain texture
addNoiseGradient Risograph-style noise gradient
addIsometricGrid Isometric grid (3 axes)
addDotGrid Notion/Linear-style dot grid
addStripes Diagonal stripe pattern
addWaveDivider Sinusoidal section divider
addProgressRing Circular progress ring + center label
addSparkline Mini inline line chart (smoothed)
addBarRace Horizontal ranked bar chart with labels & values
addRadarChart Multi-series spider/radar polygon chart
addHeatmap Grid colored by value (lerp low→high)
addFunnel Conversion funnel with trapezoid stages
addCodeBlock Syntax-highlighted code (js/ts/py/json/sh)
addIcon SVG / Lucide icon by name / raw paths

native = XML PPT (editable in PowerPoint) · raster = SVG→PNG (pixel-perfect, await required)

Architecture

    Your code (recipe.js)
            │
            ▼
         ┌──────┐
         │ Pres │   ← layout, title, save()
         └──┬───┘
            │ addSlide()
            ▼
        ┌───────┐
        │ Slide │   ← fluent API + ops[]
        └──┬─┬──┘
           │ │
    ┌──────┘ └──────┐
    ▼               ▼
  NATIVE         RASTERISED
 (PPT XML)       (SVG → PNG)
  addText         addBlob
  addRect         addGradientText
  addChart        addGlassCard
  addShape        addCodeBlock …
    │               │
    └───────┬───────┘
            ▼
       ┌──────────┐
       │ pptxgenjs│   serialises → .zip → .pptx
       └────┬─────┘
            ▼
       hello.pptx

Each Slide method is also recorded in slide._ops so pres.renderAllSvg() can reproduce the slide as an SVG — that's the live preview, 100% offline, no PowerPoint needed.

Install

npm install slidewave pptxgenjs

pptxgenjs is a peer dependency so you control its version.

Browser-only. Slidewave uses Canvas, Image and Blob to rasterize SVG primitives. It does not run in Node without a DOM shim.

Quick start

import { Pres } from 'slidewave'

const pres = new Pres({ layout: 'LAYOUT_WIDE', title: 'My deck' })
pres.setTheme({ primary: '#7C3AED', fontBody: 'Inter, system-ui, sans-serif' })

const s = pres.addSlide()

// Aurora gradient background
await s.addAuroraGradient({
  x: 0, y: 0, w: 13.333, h: 7.5,
  bg: '#0B0B0F',
  blobs: [
    { x: 0.2, y: 0.3, r: 0.45, color: '#7C3AED' },
    { x: 0.75, y: 0.25, r: 0.4, color: '#EC4899' },
    { x: 0.55, y: 0.75, r: 0.5, color: '#06B6D4' },
  ],
})

// Cover title (native — editable in PowerPoint)
s.addText('Build slides\nthat actually\nlook designed.', {
  x: 0.8, y: 1.5, w: 11, h: 4,
  fontFace: 'Fraunces, Georgia, serif',
  fontSize: 90, bold: true, color: '#FFFFFF',
})

// KPI row
s.addKPIGrid({
  x: 0.8, y: 5.8, w: 11.7, h: 1.4, gap: 0.25,
  items: [
    { label: 'REVENUE',  value: '$4.2M', delta: 28  },
    { label: 'USERS',    value: '128k',  delta: 14  },
    { label: 'NPS',      value: '72',    delta: 6   },
    { label: 'CHURN',    value: '2.1%',  delta: -18 },
  ],
  bg: 'rgba(255,255,255,0.1)', color: '#FFFFFF',
})

await pres.save('demo.pptx')

API

new Pres(options)

new Pres({
  layout?: 'LAYOUT_WIDE' | 'LAYOUT_16x9' | 'LAYOUT_16x10' | 'LAYOUT_4x3',
  title?: string, author?: string, company?: string, subject?: string,
})
Method Returns Description
addSlide(opts?) Slide Adds a new slide
size() { width, height } Slide dimensions in inches
save(filename) Promise<void> Triggers browser download
toBlob() Promise<Blob> Returns the raw .pptx blob
toArrayBuffer() Promise<ArrayBuffer> Returns raw bytes
.ShapeType, .AlignH, .AlignV pptxgenjs constants

Slide — native primitives (editable in PowerPoint)

All coordinates are in inches. 16:9 wide = 13.333 × 7.5.

slide.addText(text, opts)

slide.addText('Title', {
  x: 1, y: 1, w: 10, h: 1.2,
  fontFace: 'Fraunces',
  fontSize: 72,
  color: '#0b0b0f',
  bold, italic, underline,
  align: 'left' | 'center' | 'right',
  valign: 'top' | 'middle' | 'bottom',
  charSpacing: 2,                  // POINTS — typical 2..6, negative = tighter
  lineSpacingMultiple: 1.1,
  shadow: { type: 'outer', blur: 20, offset: 4, angle: 90, color: '#000', opacity: 0.3 },
  glow:   { size: 8, opacity: 0.5, color: '#6366f1' },
  outline:{ size: 1, color: '#000' },
})

charSpacing is in POINTS. Typical values: 2 to 6. Negative tightens. Setting it to 300 will break your layout.

slide.addRect(opts)

slide.addRect({
  x: 1, y: 3, w: 5, h: 2,
  fill: '#818cf8',                 // or { type: 'solid', color }
  radius: 0.2,                     // inches
  borderColor: '#1e1b4b', borderWidth: 1,
  shadow: { type: 'outer', blur: 40, offset: 20, angle: 90, color: '#000', opacity: 0.15 },
  rotate: 0,
})

Other native methods

slide.addShape('ellipse' | 'triangle' | ..., opts)   // any pptxgenjs shape
slide.addLine({ x1, y1, x2, y2, color, width })
slide.addImage({ x, y, w, h, data, path })
slide.raw()                                          // underlying pptxgenjs slide

Slide — rich primitives (rasterized)

These are async because they render SVG → PNG before embedding.

Backgrounds & textures

await s.addBackground({ color: '#fafaf7' })
await s.addBackground({ gradient: { type: 'mesh', colors: ['#6366f1', '#ec4899'], base: '#0b0b0f', blur: 80 } })
await s.addAuroraGradient({ x, y, w, h, bg: '#0B0B0F', blobs: [{ x:0.2, y:0.3, r:0.4, color:'#7C3AED' }], blur: 120 })
await s.addHalftone({ x, y, w, h, direction: 'radial', cellSize: 20, color: '#7C3AED', opacity: 0.4 })
await s.addGridPaper({ x, y, w, h, cellSize: 32, majorEvery: 5, opacity: 0.08 })
await s.addCheckerPattern({ x, y, w, h, cellSize: 40, colorA: '#fff', colorB: '#0b0b0f' })
await s.addRadialBurst({ x, y, w, h, rays: 24, color: '#FBBF24', opacity: 0.3 })
await s.addNoiseTexture({ x, y, w, h, opacity: 0.15, tint: '#FFFFFF' })
await s.addDotGrid({ x, y, w, h, cellSize: 24, dotRadius: 1.2, color: '#fff', opacity: 0.25 })
await s.addStripes({ x, y, w, h, angle: 45, stripeWidth: 16, color: '#fff', opacity: 0.15 })
await s.addGrain({ x, y, w, h, opacity: 0.08 })
await s.addNoiseGradient({ x, y, w, h })
await s.addBlob({ x, y, w, h, seed: 42, points: 8, fillGradient: ['#ec4899', '#f59e0b'] })
await s.addGlassCard({ x, y, w, h, tint: '#fff', tintOpacity: 0.12, highlight: true })
await s.addIsometricGrid({ x, y, w, h })

Text effects

await s.addGradientText('Hello', {
  x, y, w, h,
  fontFamily: 'Fraunces, serif', fontSize: 140, fontWeight: 500,
  gradient: ['#6366f1', '#ec4899'], angle: 135,
})

Data visualisation

await s.addBarRace({
  x, y, w, h,
  data: [{ label: 'EU', value: 4200, color: '#7C3AED' }, ...],
})

await s.addRadarChart({
  x, y, w, h,
  axes: ['Speed', 'Quality', 'Price', 'Support', 'DX'],
  values: [[0.9, 0.8, 0.7, 0.85, 0.95], [0.6, 0.7, 0.9, 0.5, 0.4]],
  fillColor: ['#7C3AED', '#9CA3AF'],
})

await s.addHeatmap({
  x, y, w, h,
  data: [[3,5,8,6], [2,7,4,9]],
  rowLabels: ['A', 'B'], colLabels: ['Q1','Q2','Q3','Q4'],
  colorLow: '#F3E8FF', colorHigh: '#7C3AED',
})

await s.addFunnel({
  x, y, w, h,
  stages: [{ label: 'Visitors', value: 100000 }, { label: 'Paying', value: 7200 }],
})

await s.addProgressRing({ x, y, w, h, value: 0.72, sublabel: 'NPS' })
await s.addSparkline({ x, y, w, h, values: [10, 14, 12, 18, 22, 28] })
await s.addCodeBlock({ x, y, w, h, code: 'const x = 1', language: 'js', theme: 'dark' })
await s.addIcon({ x, y, w, h, name: 'Zap', color: '#7C3AED' })

Layout composites (native — editable in PowerPoint)

// Stat card with delta + sparkline
s.addStatCard({ x, y, w, h, value: '$4.2M', label: 'REVENUE', delta: 28,
  sparkline: [12,15,18,22,28,38,42] })

// Grid of KPI cards
s.addKPIGrid({ x, y, w, h, gap: 0.25,
  items: [{ label: 'MRR', value: '$84k', delta: 12 }, ...] })

// Callout box
s.addCallout({ x, y, w, h, variant: 'success',
  title: 'Production-ready', body: 'Used in 200+ companies.' })

// Feature card
s.addFeatureCard({ x, y, w, h, icon: 'Zap', title: 'Fast', body: '100 slides/s' })

// Step flow 1→2→3
s.addStepFlow({ x, y, w, h,
  steps: [{ title: 'Install' }, { title: 'Write' }, { title: 'Export' }] })

// Comparison table ✓/✗
s.addComparisonTable({ x, y, w, h,
  columns: ['Free', 'Pro'], highlightCol: 1,
  rows: [{ label: 'Unlimited slides', values: [false, true] }] })

// Team card
s.addTeamCard({ x, y, w, h, name: 'Claude N.', role: 'FOUNDER',
  initials: 'CN', bio: 'Obsessed with editorial typography.' })

// Logo cloud
s.addLogoCloud({ x, y, w, h, logos: ['https://...logo.png', ...], cols: 4 })

Low-level escape hatch

Nothing from pptxgenjs is hidden:

const rawSlide = slide.raw()
rawSlide.addChart(pres._pptx.ChartType.bar, data, chartOpts)

pres._pptx.defineSlideMaster({ ... })

Theme system

const pres = new Pres({ theme: { primary: '#7C3AED', fontBody: 'Inter, sans-serif' } })
// or later:
pres.setTheme({ primary: '#FF0000' })

All primitives fall back to theme values when an option is omitted. Override explicitly to take priority.

Key Default Used by
primary #7C3AED badges, avatars, accents
text #FAFAFA addText, addTable
textDim #A1A1AB captions, roles
surface #1E1E3F card backgrounds
fontBody Inter, system-ui all text primitives
fontDisplay Fraunces, Georgia display headings
fontMono JetBrains Mono code blocks

Exposed utilities

For advanced usage — build your own primitives:

import {
  // SVG helpers
  blobSvg, halftoneSvg, auroraGradientSvg, radarChartSvg, funnelSvg,
  gradientSvg, gradientTextSvg, glassSvg, barRaceSvg, heatmapSvg,
  // Raster
  svgToPngDataUrl, generateGrainPng,
  // Colors
  normalizeHex, hexToRgb, rgbToHex, mix,
  // Cache
  cachedRaster, clearRasterCache, rasterCacheStats, setRasterCacheMax,
  // Validation
  validateRect, validateColor, safeRun, setStrict,
} from 'slidewave'

Design rules

  • Sync methods → native PPT XML → text + shapes stay editable
  • Async methods → rasterized PNG → visually impossible effects, non-editable
  • All sizes in inches (pptxgenjs-compatible)
  • All colors as #RRGGBB or RRGGBB
  • pptxgenjs is a peer dep — use any compatible version you want

Use with LLMs

Slidewave is designed so agents (Claude, GPT, Gemini, Mistral) can produce richer output than pptxgenjs allows. Give an LLM the API reference above as part of your system prompt, and it will emit code that produces editorial-grade slides.

Development

git clone https://github.com/NdandaClaude/slidewave
cd slidewave
npm install
npm run dev              # demo playground at localhost:5173
npm run build:lib        # build library to dist/
npm run pack:check       # verify npm package contents

Roadmap

  • Node-compatible rasterizer (via @napi-rs/canvas) for SSR / CI use
  • Monaco editor in the playground
  • addAnimatedGif — export animated sequences
  • TypeScript types for all v0.5 new methods
  • Official LLM system-prompt snippet

License

MIT © 2026 Claude Ndanda