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

zhuzhiliao.js

v0.5.0

Published

A physics-driven bamboo cicada that floats over any web page.

Downloads

1,386

Readme

zhuzhiliao

npm license: MIT

在线试玩 · 互动科学报告 · npm 包

一个可以外挂到任意网页上的悬浮竹知了。默认直接可玩,同时允许替换知了 DOM、杆子 DOM、声音实现、渲染器与物理参数。

  • 运行时零依赖
  • Web Component + Shadow DOM
  • 组件画面聚焦杆、绳和知了,宿主页面保持原有布局
  • 指针驱动的绳系质点物理
  • Web Audio 程序化合成声音;转速、张力、旋转方向和材料参数共同改变音色
  • 素材与逻辑随包本地运行

代码、声音与视觉均为独立原创实现;参考项目 imsai-sh/zhuzhiliao 用于概念与架构研究。

最快使用

pnpm add zhuzhiliao
import { mountBambooCicada } from 'zhuzhiliao';

mountBambooCicada(); // 默认挂载到 document.body,透明坐标层覆盖整个视口

host-first 调用兼容现有项目:

mountBambooCicada(document.querySelector('#some-host')!);

也可以声明式使用:

<script type="module">
  import 'zhuzhiliao';
</script>

<bamboo-cicada></bamboo-cicada>

交互

抓住杆子或知了本体,按住后画圈。输入移动杆端锚点,知了作为独立质点受到:

  • 重力;
  • 空气阻力;
  • 拉伸阶段生效的弹性绳张力;
  • 绳方向径向阻尼。

每帧内部以固定小步长积分,页面掉帧时也会限制最大能量注入。松手后,知了继续靠惯性摆动并逐渐停下。默认手势位移有 1.45× 增益,方便触屏用较小的拇指圈甩响;可用 inputGain 调整或设为 1

const toy = mountBambooCicada({ inputGain: 1.2 });
toy.startAuto();
toy.stopAuto();
toy.setAnchor(520, 180); // 当前视口内的 CSS 像素坐标
console.log(toy.motion);

是否会变音?

会。默认 SynthCicadaVoice 使用绳方向角速度、绳长比、转动相位和活动强度共同驱动一套 reduced-order physical model(降阶物理模型):

  • 在拟合状态 2.367 r/s 下,每秒约产生 78 个主要 stick-slip 事件,即每圈约 33 次;
  • 脉冲间隔使用 CV ≈ 0.25 的确定性不规则 renewal process,避免机械式等周期振荡;
  • 两个膜/膜—空气有效模态为 1506.37 Hz / Q 10.491760.85 Hz / Q 10.62
  • 小膜辐射先经过 1863.85 Hz 高通,压住不合理的 300–800 Hz 低频机械位移能量;
  • 一条 108.86 mm 的有损竹筒支路以 0.40 反射、1.50 损耗和 0.45 反相耦合叠回直达声,产生短 group delay 与空心脉冲尾;
  • 每圈叠加深度约 0.66 的一次 AM 与 0.35 的二次 AM;
  • 单位峰值 Web Audio bandpass 后使用 32× 模态 make-up,并在末端用 2× oversampled tanh soft limiter 保留默认响度、约束极限参数;
  • 绳松弛时音量门控为零;张紧后,张力与结构参数连续改变模态、管口辐射和输出亮度。

映射函数 mapVoiceParameters(state) 与只读拟合参数 defaultCicadaFit 均为公开 API,开发者可以复用同一运动状态连接自己的采样器或音频引擎。默认实现只使用程序化激励和 Web Audio 节点。

替换知了或杆子的 DOM

JavaScript

const cicada = document.createElement('img');
cicada.src = '/my-cicada.webp';
cicada.alt = '我的竹知了';
cicada.style.width = '80px';

const pole = document.createElement('div');
pole.className = 'my-pole';

mountBambooCicada({
  parts: {
    cicada: { source: cicada, socket: { x: 0.5, y: 0.052 } },
    pole: { source: pole, socket: { x: 0.5, y: 0 } },
  },
});

socket 是 DOM 内部的归一化连接点:(0, 0) 为左上角,(1, 1) 为右下角。renderer 会先把 socket 对齐到物理端点,再围绕该点旋转;因此图片尺寸或长宽比变化时,绳端不会漂到透明区域。传入已有 DOM 会移动节点而不是克隆,原有事件监听器仍然保留;运行时传入 null 可恢复默认皮肤:

toy.configure({ parts: { cicada: null, pole: null } });

多实例时建议使用 factory,每只玩具都会获得新节点:

mountBambooCicada({
  parts: {
    cicada: () => document.querySelector<HTMLTemplateElement>('#my-cicada')!.content.firstElementChild!.cloneNode(true) as HTMLElement,
    pole: () => document.createElement('my-bamboo-pole'),
  },
});

HTML slots

<bamboo-cicada>
  <img slot="cicada" data-bc-socket="0.5,0.052" src="/my-cicada.webp" alt="我的竹知了" />
  <div slot="pole" data-bc-socket="0.5,0" class="my-pole"></div>
</bamboo-cicada>

组件变换 slot 外层包装器,你的 DOM 内容和内部样式继续由应用控制。

使用上传或采样音频

SampledCicadaVoice 与默认合成器实现同一个 CicadaVoice 接口,因此可以直接接入现有物理状态。下面的文件只在当前浏览器内解码;库不会上传、持久化或请求任何远程服务:

import { SampledCicadaVoice, mountBambooCicada } from 'zhuzhiliao';

const toy = mountBambooCicada();
const input = document.querySelector<HTMLInputElement>('#audio-file')!;

input.addEventListener('change', async () => {
  const file = input.files?.[0];
  if (!file) return;

  const voice = new SampledCicadaVoice({
    volume: 1,
    motionAmount: 1,
    pitchAmount: 0.35,
    filterAmount: 0.72,
    loop: true,
  });

  // 先在用户手势内 unlock,兼容 iOS;随后才读取/解码文件。
  await voice.unlock();
  await voice.load(file);
  toy.configure({ voice: () => voice });
});

调制沿用同一个 MotionState

| 物理量 | 采样音频参数 | | --- | --- | | activity + 绳张紧度 | 输出 gain envelope | | abs(angularVelocity) | playbackRate;受 pitchAmount 控制 | | rope.angle | 每圈一次/二次 amplitude modulation | | 速度 + 张紧度 | low-pass cutoff;受 filterAmount 控制 |

pitchAmount: 0 可保留原音高;motionAmount: 0 可让音频不随运动静音;运行时可用 configure() 连续调整。load() 接受 BlobFileArrayBuffer。远程音频可由应用先自行 fetch()Blob,并遵守来源的 CORS 与授权条款。

默认物理音频

默认声音采用轻量 source–filter physical model:

stick-slip pulse + friction noise
              ↓
  membrane modes + radiation HPF
              ↓
 direct + lossy bamboo mouth path
              ↓
     rotation AM + output rolloff

SynthCicadaVoice 开放 volume(默认 2.5×,范围 0.25–4×)、frictionmembraneTensiontubeLengthtubeDiameter,实时调整最终响度、松香摩擦、膜面张力与竹筒腔体。首次指针或键盘操作会解锁 Web Audio。

import type { CicadaVoice, MotionState } from 'zhuzhiliao';

class SampleVoice implements CicadaVoice {
  unlock() {
    // 在用户手势中创建或恢复你的 AudioContext
  }
  update(state: Readonly<MotionState>) {
    // 使用 state.rope.angularVelocity / tension / angle 驱动采样器
  }
  silence() {}
  destroy() {}
}

mountBambooCicada({
  voice: () => new SampleVoice(),
});

所有权约定:

  • 传 factory:实例由组件拥有,destroy() 时一并销毁;
  • 直接传 voice 对象:视为外部共享资源,组件仅调用 silence()

调整物理

mountBambooCicada({
  physics: {
    ropeLength: 150,
    gravity: 820,
    stiffness: 2500,
    radialDamping: 17,
    airDrag: 0.6,
  },
});

完整类型:PhysicsOptionsPhysicsStateMotionStateRopeState

替换完整渲染器

import type { CicadaRenderer } from 'zhuzhiliao';

const renderer: CicadaRenderer = {
  mount({ root, host }) {
    // 可在这里建立 Canvas、SVG、Three.js 或任意 DOM 渲染层
  },
  render(state) {
    // state 是统一的杆端、质点、绳和发声活动状态
  },
  destroy() {},
};

mountBambooCicada({ renderer });

DefaultCicadaRenderer 提供开箱即用的 DOM 表现。3D 可以作为独立 renderer 包接入,并复用核心物理与音频状态。

Renderer 所有权与 voice 一致:factory 返回的实例由组件销毁,直接传入的对象适合作为外部共享资源。

本地试玩

pnpm install
pnpm dev

试玩页是一张普通的本地网页,竹知了通过 mountBambooCicada() 额外挂载;网络面板保持零外部素材与后端请求。

验证

pnpm test
pnpm typecheck
pnpm build
pnpm pack

License

MIT