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

now-playing-glass

v0.3.2

Published

A presentational React "now playing" widget — an album disc that taps open into a Dynamic-Island-style glass card (shared-element cover morph, equalizer, ambient backdrop, marquee). Bring your own data source (Last.fm, Spotify, Apple Music…).

Readme

now-playing-glass — 正在聽玻璃小卡

A presentational React "now playing" widget: an album disc that taps open into a Dynamic-Island-style glass card — shared-element cover morph, bouncing equalizer, blurred-cover ambient backdrop, gentle title marquee, live halo. Bring your own data source (Last.fm, Spotify, Apple Music, anything).

English | 繁體中文

🔗 Live demo · in the wild: kvcc.me (the disc at the top-left)

now-playing-glass


English

Why

It's purely visual. You hand it a song; it renders the widget. No fetching, no polling, no provider lock-in — pair it with whatever scrobbler/API you like.

Install

npm i now-playing-glass

react (≥18) is a peer dependency (your app already provides it). framer-motion ships as a dependency, so it installs automatically — nothing else to add.

Use

import { NowPlaying } from 'now-playing-glass';
import 'now-playing-glass/styles.css';

function Header({ song, refetch }) {
  return (
    // `song` shape: { name, artist, art?, url?, nowplaying?, color? }
    <NowPlaying
      song={song}            // null → renders nothing
      variant="mini"         // "mini" (disc → card morph) | "card" (static chip)
      align="left"           // "left" | "right" — flip for right-corner placement
      lang="en"              // built-in labels: "en" | "zh"
      onRefresh={refetch}    // fired on open — re-fetch so it shows the live track
    />
  );
}

You can wire your own fetching, or use the bundled useNowPlaying hook, which handles initial load + polling + pause-while-hidden + on-demand refresh (see below). Either way the widget stays presentation-only.

Props

| Prop | Type | Default | Notes | |---|---|---|---| | song | Song \| null | — | null or no name → renders nothing | | variant | "mini" \| "card" | "card" | mini = disc that morphs open; card = static chip | | align | "left" \| "right" \| "auto" | "left" | (mini) expand direction. "right" for a right-hand corner; "auto" picks the side with more room (measured on open) | | lang | "en" \| "zh" | "en" | built-in label language | | labels | { nowPlaying?, recentlyPlayed? } | — | override labels for full i18n | | onRefresh | () => void | — | called when opened — re-fetch your data here | | open / onOpenChange | boolean / (open) => void | — | (mini) make the open state controlled; onOpenChange fires on every change | | openOnHover | boolean | false | (mini) open on hover (desktop pointers) | | className | string | — | extra class on the root |

Song: { name, artist, art?, url?, nowplaying?, color?, progress?, duration? }. Set nowplaying: true for the live treatment (halo + spinning cover + bouncing equalizer). color tints the halo/equalizer (falls back to green). Set duration (and progress) in ms to show a playback progress bar.

useNowPlaying (optional)

Data-source-agnostic plumbing so you don't re-implement load + poll + refresh:

import { NowPlaying, useNowPlaying } from 'now-playing-glass';

const { song, refresh } = useNowPlaying({
  // Return a Song, `null` to clear, or `undefined` to keep the current track.
  // `fresh` is true on user-triggered refreshes — bust your cache then.
  fetcher: ({ fresh }) =>
    fetch(fresh ? `/api/now-playing?t=${Date.now()}` : '/api/now-playing')
      .then(r => (r.ok ? r.json() : null))
      .then(d => d?.song ?? null),
  interval: 25000, // poll ms; pauses while the tab is hidden; 0 disables
});

<NowPlaying song={song} variant="mini" onRefresh={refresh} />;

It only touches react — your data source (Last.fm, Spotify, your own API route) stays entirely yours, so no provider lock-in and no exposed API keys.

useLastfm (optional, drop-in)

The most common source, wired for you (Last.fm read keys are safe client-side):

import { NowPlaying } from 'now-playing-glass';
import { useLastfm } from 'now-playing-glass/lastfm';

const { song, refresh } = useLastfm({ user: 'YOUR_USER', apiKey: 'YOUR_KEY' });
<NowPlaying song={song} variant="mini" onRefresh={refresh} />;

Draggable (recipe)

Dragging isn't built in (a now-playing widget is usually a fixed corner element). If you want it, wrap the widget in your own draggable container — align="auto" keeps the card expanding toward the side with room as it moves:

import { motion } from 'framer-motion';

<motion.div drag dragMomentum={false} style={{ position: 'fixed', top: 16, left: 16 }}>
  <NowPlaying song={song} variant="mini" align="auto" onRefresh={refresh} />
</motion.div>;

Theming

Self-contained out of the box. It reads --label, --fill and liquid-glass-kit's --lg-* tokens when present, so it melts into an existing design system; otherwise the baked light/dark defaults apply. Dark mode follows prefers-color-scheme, and an ancestor [data-theme="light|dark"] overrides it. Respects prefers-reduced-motion.

The refraction is frosted-glass via backdrop-filter; it degrades gracefully where that isn't supported.

More by kv

Part of the kvcc.me ecosystem of small, focused libraries.

Credit

The Dynamic-Island morph idea is Apple's; the glass material follows the liquid-glass family. Built by kv.


繁體中文

這是什麼

一個純呈現的 React「正在聽」小卡:頂部一顆專輯封面圓盤,點開以 Dynamic-Island 式形變展開完整玻璃卡 —— 共享封面 morph、動態等化器、模糊封面 氛圍底、歌名輕柔捲動、live 光環。資料來源自己接(Last.fm / Spotify / Apple Music 都行)。

安裝

npm i now-playing-glass

react(≥18)是 peer dependency(你的 app 已提供);framer-motion 是一般依賴、會自動裝,不用再加。

用法

import { NowPlaying } from 'now-playing-glass';
import 'now-playing-glass/styles.css';

// song 形狀:{ name, artist, art?, url?, nowplaying?, color? }
<NowPlaying
  song={song}            // null → 不渲染
  variant="mini"         // "mini"(圓盤→卡片形變) | "card"(靜態 chip)
  align="left"           // "left" | "right" —— 放右上角就用 "right",卡片才不會撐出畫面
  lang="zh"              // 內建文案語言:"en" | "zh"
  onRefresh={refetch}    // 點開時觸發 —— 在這裡重抓資料,卡片才顯示「現在」這首
/>

它不抓資料:自己接 song,或用內建的 useNowPlaying hook(幫你處理 首載+輪詢+背景分頁暫停+手動刷新,見下方)。

Props

| Prop | 型別 | 預設 | 說明 | |---|---|---|---| | song | Song \| null | — | null 或沒有 name → 不渲染 | | variant | "mini" \| "card" | "card" | mini=圓盤形變展開;card=靜態 chip | | align | "left" \| "right" \| "auto" | "left" | (mini) 展開方向;右側角落用 "right""auto" 開卡時量空間自動挑邊 | | lang | "en" \| "zh" | "en" | 內建文案語言 | | labels | { nowPlaying?, recentlyPlayed? } | — | 覆寫文案,完整 i18n 控制 | | onRefresh | () => void | — | 點開時呼叫 —— 在此重抓資料 | | open / onOpenChange | boolean / (open) => void | — | (mini) 受控開合;onOpenChange 每次變更觸發 | | openOnHover | boolean | false | (mini) 桌機滑鼠移上去就展開 | | className | string | — | 根元素額外 class |

Song{ name, artist, art?, url?, nowplaying?, color?, progress?, duration? }nowplaying: true 進 live 樣式(光環+封面慢轉+等化器跳動);color 為光環/等化器上色(沒給退綠); 給 duration(+progress)(ms)會畫播放進度條。

useNowPlaying(選用)

與資料源無關的 plumbing,免得每次重寫「首載+輪詢+刷新」:

import { NowPlaying, useNowPlaying } from 'now-playing-glass';

const { song, refresh } = useNowPlaying({
  // 回傳 Song、null(清空) 或 undefined(維持現狀)。fresh=true 是使用者手動刷新 → 繞快取
  fetcher: ({ fresh }) =>
    fetch(fresh ? `/api/now-playing?t=${Date.now()}` : '/api/now-playing')
      .then(r => (r.ok ? r.json() : null))
      .then(d => d?.song ?? null),
  interval: 25000, // 輪詢 ms;背景分頁自動暫停;0 = 不輪詢
});

<NowPlaying song={song} variant="mini" onRefresh={refresh} />;

只依賴 react,資料源(Last.fm / Spotify / 自家 API)完全是你的,不綁特定供應商、不外洩 key。

useLastfm(選用、即插即用)

最常見的來源直接幫你接好(Last.fm read key 可安全放前端):

import { NowPlaying } from 'now-playing-glass';
import { useLastfm } from 'now-playing-glass/lastfm';

const { song, refresh } = useLastfm({ user: 'YOUR_USER', apiKey: 'YOUR_KEY' });
<NowPlaying song={song} variant="mini" onRefresh={refresh} />;

可拖曳(recipe)

拖曳不內建(now-playing 多半是固定角落元件)。要的話用自己的拖曳容器包起來, align="auto" 會隨它移動自動往有空間的那側展開:

import { motion } from 'framer-motion';

<motion.div drag dragMomentum={false} style={{ position: 'fixed', top: 16, left: 16 }}>
  <NowPlaying song={song} variant="mini" align="auto" onRefresh={refresh} />
</motion.div>;

主題

開箱即用、自帶樣式。偵測到 --label--fill 或 liquid-glass-kit 的 --lg-* token 就吃它們,自然融入既有設計系統;沒有就用內建的明/暗預設。深色跟隨 prefers-color-scheme,祖層 [data-theme="light|dark"] 可覆寫。尊重 prefers-reduced-motion

折射用 backdrop-filter 霜化玻璃,不支援的環境會優雅降級。

More by kv

kvcc.me 生態系裡一組小而專注的共用庫之一。

授權

Dynamic-Island 形變的點子源自 Apple;玻璃材質沿用 liquid-glass 家族。由 kv 製作。