← Back to home@Rafayellllllllllllll

gesture-flower

手势控制粒子花朵绽放 · Three.js + MediaPipe Hands + GLSL

Stars
0
Language
JavaScript
Created
Oct 23, 2026
Updated
Oct 23, 2026

Introduction

指尖生花 · Gesture Bloom

用摄像头识别手势,让一朵发光粒子花在你指尖绽放。

握拳 → 花朵收拢成花苞;张开手掌 → 粒子向外绽放;手的位置会带动花朵轻微跟随。 纯 Three.js + MediaPipe Hands + GLSL,无构建步骤,本地起个 HTTP 服务就能跑。

花苞 半开 绽放

花苞 · 半开 · 绽放

快速开始

摄像头 API 只在 https 或 localhost 下可用,所以不能直接双击 index.html。

# 方式一:Windows 双击 serve.cmd(自动找 node / python)
# 方式二:手动
node serve.mjs . 8787
# 方式三:只有 Python
python -m http.server 8787 --bind 127.0.0.1

然后打开 http://localhost:8787/,浏览器会请求摄像头权限,允许即可。

依赖已全部本地化到 vendor/(three 0.186、@mediapipe/hands 0.4), 不依赖 CDN,国内网络也能离线跑。约 13MB。

手势与 tension

tension 是手掌张开度,范围 0 ~ 1:0 = 握拳,1 = 完全张开。

计算方式(src/hands.js → computeTension):

palmSpan   = |wrist(0) − middle_MCP(9)|            // 手掌尺度,用于归一化,抵消远近
ratio_i    = |wrist(0) − tip_i| / palmSpan         // 四指:tip ∈ {8,12,16,20}
meanRatio  = mean(ratio_i)                         // 握拳≈1.2~1.5,张开≈2.2~2.6
raw        = clamp((meanRatio − 1.12) / (2.30 − 1.12), 0, 1)
t          = smoothstep(raw)                        // 3t²−2t³,曲线更柔

再做一次时间平滑,避免临界抖动让花朵抽搐:

tension += (target - tension) * 0.18

张开度映射到视觉(src/main.js):

tension状态
< 0.32花苞闭合
0.32 ~ 0.66平滑过渡(花瓣扭转外翻)
> 0.66完全绽放

绽放程度本身用需求指定的缓动:

spread += (targetSpread - spread) * 0.1

URL 参数

参数默认说明
count16000粒子数量,范围 5000~20000
petals6花瓣数量,3~12
radius1.95花朵半径
seed20261023随机种子,同种子生成同一朵花
spread—固定绽放程度(0~1),用于截图/调参
camera—off 关闭摄像头,进入鼠标模式
debug—1 显示 fps / spread / 手部状态

例:http://localhost:8787/?count=20000&petals=8&spread=0.8

快捷键

键作用
L显示 / 隐藏 21 点手部骨架
V摄像头在小窗 与 低透明度全屏背景 之间切换
D调试信息
R重连摄像头

实现要点

花瓣形状(src/flower.js)用显式参数化,而不是"半径阈值":

u ∈ [0,1]   沿花瓣,0 = 花心,1 = 尖端
v ∈ [-1,1]  横向,±1 是花瓣边缘
halfW(u) = 0.52 · sin(π·u^0.75)^0.70 · (1 − 0.16u)
θ = p·2π/P + tilt[p] + v · halfW(u)
r = R · len[p] · (0.10 + 0.90u)

每片花瓣的长度/角度都有 ±6% 抖动,避免"完美对称的假花"。

花苞用同一套 θ / u 参数,只是收进一个竖立的水滴轮廓(上下收敛、中段最宽) 并按高度绕垂直轴扭转——所以中间态看到的是花瓣向外翻开,而不是一团球膨大。

位置插值全部在顶点着色器(src/shaders.js)完成:CPU 每帧只更新 uSpread / uTime / uHand 三个 uniform,16k 粒子也不会有 JS 开销。

错峰绽放:每颗粒子按 aRho、aSeed 分配一点点延迟,花心先开、瓣尖后开, 中间态才不是一团模糊。叠加混合(additive)容易过曝,所以单颗粒子压得很暗、 靠数量堆出光感,并且花苞态整体压暗、绽放态提亮,保证两种状态的亮度都稳定。

配色(顶点着色器 palette(),按 aRho 分层):

区域颜色占比
花心#FF9ECF + #A7E8FF点缀
花瓣主体#FF7AB6 → #FF9ECF~60%
花瓣外缘#8FD3FF → #B388FF~25%
最外一圈#9B6BFF~15%

不用深蓝、荧光绿、暖黄,底色纯黑。

目录结构

gesture-flower/
├─ index.html            页面骨架 + importmap
├─ serve.cmd             Windows 一键起服务
├─ serve.mjs             零依赖静态服务器(正确 MIME,含 .wasm/.data)
├─ src/
│  ├─ main.js            场景、动画循环、HUD、骨架绘制、兜底交互
│  ├─ flower.js          粒子几何:花瓣/花蕊/花苞/背景星尘
│  ├─ hands.js           MediaPipe 封装 + tension 计算
│  ├─ shaders.js         顶点/片元着色器(GLSL)
│  └─ style.css          HUD 与响应式
├─ vendor/
│  ├─ three/             three.module.js + three.core.js
│  └─ mediapipe/         hands.js + wasm/data(SIMD 版)
└─ docs/                 截图

兼容性与限制

  • Chrome / Edge / Safari 16.4+ / Firefox 均可用;需要 WebGL 与 getUserMedia。
  • 移动端浏览器要求 https(localhost 除外),并且只有前/后摄像头之一会被打开。
  • 摄像头不可用时(拒绝授权、没有设备、被占用)会自动切到鼠标模式: 左右移动鼠标即可控制绽放,页面仍可演示。
  • 只为 1 只手做识别(maxNumHands: 1),多人场景取画面里最靠前的一只。
  • MediaPipe 的手部模型在弱光/快速移动时会丢点,已用 400ms 宽限 + 时间平滑吸收。

许可与致谢

MIT(见 LICENSE)。第三方: three.js(MIT)、 MediaPipe Hands(Apache-2.0)。 vendor/ 下为两者的原样分发,遵循各自许可。

任何设备、任何人点开就能玩

页面不强制摄像头,进来先是可以直接玩的开始界面:

  • 开启摄像头 · 用手势玩 → 点了才申请权限(iOS Safari 要求在用户手势里调用, 所以不能自动弹权限框)。拿到画面后:握拳=花苞,张开=绽放。
  • 不开启摄像头 · 触屏 / 鼠标玩 → 立刻可玩,不需要任何权限:
    • 手机:按住屏幕 = 绽放,松开 = 收拢;按住拖动花朵会跟着手指走。
    • 桌面:不按键左右移动鼠标也能控制;按住则全开。
  • 摄像头失败(拒绝授权 / 没有设备 / 被占用 / 非 https)会弹提示,并自动落到触屏模式, 不会卡在空白页。

线上地址走 GitHub Pages,是 https,所以手机上摄像头也能直接用: https://rafayellllllllllllll.github.io/gesture-flower/

性能:手机自动降到 10000 粒子并把渲染分辨率限制在 1.6× DPR,保证 60fps 手感。