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 🙏

© 2025 – Pkg Stats / Ryan Hefner

musify

v1.2.3

Published

Powerful and elegant music image generator for Discord bots featuring high performance, customizable designs, and seamless integration.

Downloads

469

Readme

🎧 Musify

Professional Music Image Generator for Discord Bots
Create stunning, high-performance music cards with elegant glass design and automatic color extraction

npm version npm downloads License Node.js

👀 Preview


✨ Features

| Feature | Description | |---------|-------------| | 🔎 Liquid Glass Design | Modern, elegant music card with liquid glass effects - soft shadows, subtle strokes, and glass-like progress bars | | 🎯 Smart Color Extraction | HSL-based color calibration that preserves original hues while optimizing brightness for perfect visibility | | 💎 Glass Morphism Effects | Text with liquid glass styling (semi-transparent strokes + soft shadows) for premium readability | | 📊 Enhanced Progress Bar | Glass-effect progress bar with subtle glow, highlights, and perfect alignment with time labels | | 🎨 Adaptive Color Calibration | Intelligently adjusts color brightness while maintaining original color character (orange stays orange!) | | 🪅 Background Effects | Blur, darkness, and overlay controls for stunning visuals | | 📏 Flexible Scaling | Scale from 0.5x to 2x for different use cases | | ⚡ High Performance | Optimized rendering with minimal memory footprint | | 🔧 TypeScript Ready | Full TypeScript definitions and IntelliSense support | | 🎵 Riffy Integration | Perfect compatibility with Riffy music library | | 🖼️ Multiple Formats | Support for URLs, local paths, and buffer inputs | | 📍 Smart Layout | Intelligent progress bar and time label positioning for both left and right thumbnail placements |

🤔 Quick Start

Installation

npm install musify

Basic Usage

import { Glass } from 'musify';

const Musify = await Glass({
  thumbnailImage: 'https://example.com/thumbnail.jpg',
  name: 'Bohemian Rhapsody',
  author: 'Queen',
  requester: 'MusicLover#1234',
  progress: 45,
  startTime: '2:15',
  endTime: '5:55'
});

Send to Discord Channel

await channel.send({
  files: [{ 
    attachment: Musify, 
    name: 'now-playing.png' 
  }]
});

Generates a professional music card and returns a PNG buffer.

Configuration Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | thumbnailImage | string | - | Required: URL or path to thumbnail | | thumbnail | 'left' \| 'right' | 'left' | Thumbnail position (left or right side) | | name | string | 'Musify' | Song title (auto-truncated) | | author | string | 'Unknown Artist' | Artist name | | requester | string | 'Unknown' | User who requested the song | | progress | number | 0 | Progress percentage (0-100) | | startTime | string | '0:00' | Current time (MM:SS) | | endTime | string | '0:00' | Total duration (MM:SS) | | scale | number | 1 | Scale factor (0.5-2) | | backgroundColor | string | '#1c1c1c' | Fallback background color | | backgroundImage | string | - | Background image URL | | backgroundBlur | number | 10 | Background blur (0-50) | | backgroundDarkness | number | 0.9 | Background darkness (0-1) | | nameColor | string | 'auto' | Title color (auto-extracted with HSL calibration) | | authorColor | string | '#FFFFFF' | Artist text color | | requesterColor | string | 'auto' | Requester text color (auto-extracted) | | progressColor | string | 'auto' | Progress bar color (auto-extracted) | | timeColor | string | '#FFFFFF' | Time labels color | | style | 'v1' \| 'v2' | 'v2' | Card style version (v2 recommended) |


🎯 Usage Examples

Auto Color Extraction with Smart Calibration

const Musify = await Glass({
  thumbnailImage: track.info.thumbnail,
  name: track.info.title,
  author: track.info.author,
  requester: track.info.requester.username,
  progress: 30,
  startTime: '1:30',
  endTime: '4:15',
  // Colors automatically extracted from thumbnail
  // Uses HSL-based calibration to preserve original hues
  // Orange stays orange, not yellow! Perfect brightness for visibility
  nameColor: 'auto',
  progressColor: 'auto',
  requesterColor: 'auto'
});

Color Calibration Features:

  • ✅ Preserves original color hue and saturation
  • ✅ Adaptive brightness adjustment (not too bright, not too dark)
  • ✅ Maintains color character (orange stays orange, blue stays blue)
  • ✅ Optimized for dark backgrounds with perfect visibility

Custom Styling

const Musify = await Glass({
  thumbnailImage: 'https://example.com/thumbnail.jpg',
  name: 'Custom Styled Track',
  author: 'Custom Artist',
  requester: 'StylishUser#5678',
  progress: 75,
  startTime: '3:20',
  endTime: '4:30',
  scale: 1.2,
  backgroundColor: '#2a2a2a',
  nameColor: '#FF6B35',
  authorColor: '#E8E8E8',
  requesterColor: '#FF6B35',
  progressColor: '#FF6B35',
  timeColor: '#CCCCCC'
});

Background Effects

const Musify = await Glass({
  thumbnailImage: track.info.thumbnail,
  backgroundImage: track.info.thumbnail, // Use same image as background
  backgroundBlur: 20, // Strong blur effect
  backgroundDarkness: 0.7, // Moderate darkness
  name: track.info.title,
  author: track.info.author,
  requester: track.info.requester.username,
  progress: 50,
  startTime: '2:45',
  endTime: '5:30'
});

💎 Visual Enhancements

Liquid Glass Effect

Musify features a premium liquid glass design with:

  • Text Styling: All text elements (title, author, requester, time labels) feature:

    • Semi-transparent dark strokes for depth
    • Soft shadows for readability
    • Perfect contrast on any background
  • Progress Bar: Enhanced with glass morphism:

    • Subtle glow effect matching the progress color
    • White highlight overlay for glass-like surface
    • Semi-transparent background with glass effect
    • Perfect alignment with time labels
  • Smart Positioning:

    • Progress bar and time labels automatically adjust when thumbnail is on the right
    • No overlaps, perfect spacing
    • Time labels centered vertically with progress bar

Thumbnail Positioning

const Musify = await Glass({
  thumbnailImage: track.info.thumbnail,
  thumbnail: 'right', // or 'left' (default)
  name: track.info.title,
  author: track.info.author,
  requester: track.info.requester.username,
  progress: 50,
  startTime: '2:30',
  endTime: '5:00'
});

The layout automatically adjusts:

  • Text positioning based on thumbnail side
  • Progress bar width and position
  • Time label alignment
  • All elements perfectly spaced

🎵 Riffy Integration

Perfect integration with Riffy music library:

import { Glass } from 'musify';

// Track start event
client.riffy.on('trackStart', async (player, track) => {
  const Musify = await Glass({
    thumbnailImage: track.info.thumbnail,
    name: track.info.title,
    author: track.info.author,
    requester: track.info.requester.username,
    progress: 0,
    startTime: '0:00',
    endTime: formatTime(track.info.length),
    scale: 1.1
  });
  
  // Send to Discord
  await player.textChannel.send({
    files: [{ attachment: Musify, name: 'now-playing.png' }]
  });
});

// Progress updates
setInterval(async () => {
  if (player.current && player.playing) {
    const progress = (player.position / player.current.info.length) * 100;
    const startTime = formatTime(player.position);
    const endTime = formatTime(player.current.info.length);
    
    const Musify = await Glass({
      thumbnailImage: player.current.info.thumbnail,
      name: player.current.info.title,
      author: player.current.info.author,
      requester: player.current.info.requester.username,
      progress,
      startTime,
      endTime,
      scale: 1.1
    });
    
    // Update message
    await message.edit({ files: [{ attachment: Musify, name: 'now-playing.png' }] });
  }
}, 5000); // Update every 5 seconds

function formatTime(ms: number): string {
  const minutes = Math.floor(ms / 60000);
  const seconds = Math.floor((ms % 60000) / 1000);
  return `${minutes}:${seconds.toString().padStart(2, '0')}`;
}

⚡ Performance Optimization

Reuse Configuration Objects

const baseConfig = {
  scale: 1.1,
  backgroundColor: '#1c1c1c',
  authorColor: '#FFFFFF',
  timeColor: '#CCCCCC'
};

// Reuse for different tracks
const Musify1 = await Glass({
  ...baseConfig,
  thumbnailImage: track1.info.thumbnail,
  name: track1.info.title,
  author: track1.info.author,
  progress: 30
});

const Musify2 = await Glass({
  ...baseConfig,
  thumbnailImage: track2.info.thumbnail,
  name: track2.info.title,
  author: track2.info.author,
  progress: 60
});

Cache Generated Cards

const cardCache = new Map();

async function getCachedCard(trackId: string, progress: number) {
  const cacheKey = `${trackId}-${Math.floor(progress / 10)}`; // Cache by 10% intervals
  
  if (cardCache.has(cacheKey)) {
    return cardCache.get(cacheKey);
  }
  
  const Musify = await Glass(options);
  cardCache.set(cacheKey, Musify);
  
  // Limit cache size
  if (cardCache.size > 100) {
    const firstKey = cardCache.keys().next().value;
    cardCache.delete(firstKey);
  }
  
  return Musify;
}