4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

amazarashiのライブでやってたペンライトの動きってどうやってやってるの?!?!?

4
Last updated at Posted at 2025-12-25

こんにちは!watnowに所属しています,立命館大学4回生のumiyuri777と言います!
watnowの活動時間とバイトがダダ被りしてるので全然行けておらず,完全に幽霊部員になってます...

watnowアドベントカレンダーの並びをみてるとめちゃくちゃ真面目に技術の話をしている人が多かったので,おふざけ記事でめちゃくちゃハードル下げに行きたいと思います!!!!!

はじめに

みなさんライブは好きですか???
僕は大好きです
特にamazarashiが語彙力なくなるぐらいには好きです
4月の横アリ公演まじやばかったっす
特に観客を巻き込む演出に本当に感動しました

この動画の27秒目あたりから見えるように,客席が光ってるんですが,これ実は観客のスマホのライトを公式アプリから制御して光らせています

どうやって実現してるんだ???

さて,実際にどうやって実現してるんでしょう?
表示されるアニメーションは最初からアプリに入ってるとしても,曲の開始タイミングが正確に変わらないとライトの点灯タイミングが綺麗に合いませんよね🤔
amazarashiの例の横アリ公演では確か5万人規模でした.
5万台のスマホが同時に通信なんてしたら多分めちゃくちゃ輻輳が起こってまともに通信できないような気が...

輻輳って何?

ライブ会場とか初詣の神社とかで,スマホの通信めっちゃ重くなる時ありませんか?
それはおそらく大体のケースで輻輳が起こっているせいです!

ネットワークの分野の用語で,めちゃ簡単に言うと「通信が混雑してて渋滞している状態」状態のことです

image.png
画像引用元

あんなに狭い空間で5万台も通信しようとすると流石に帯域足りない気がします
なので,純粋にhttpのコネクションはるんじゃなくて,他の通信方法を考えないといけないです🤔

ライブ会場特有で,会場全体に同時に確実に届くもの...?
だ!!!!!

ライブだからこそ音で通信する

これ自分的には目から鱗でした
ライブなので当然音楽は聞こえますし,すべての席にほとんどラグなく届くし,帯域混み合うというか音源がその帯域を占領してる!笑

amazarashiのライブではノイズのような音をつかっていました
該当箇所は以下の箇所です

ノイズのような高い音が聞こえましたか???
おそらくこのノイズをスマホのマイクから認識して,曲が開始したこと理解するんだと思います

実際にいま公式アプリを立ち上げた状態で動画をスピーカーで流すと,ライブ会場と同じような挙動をしてくれます!
ライブ会場でなくてもちゃんとスマホのマイクは機能しますからね笑

たぶん実際のライブでは,人間が聞こえない帯域のノイズとか出していて,曲ごとに周波数を変えていたのかな?と考えてます

送る情報は最小限に

http通信みたいに文字列を送るなんて便利なことは音だけでは難しいです
今回の映像であれば,場所によって光るタイミングが違いますが,これはアプリ側で事前に自分の席情報を設定していました
アプリ側では曲の開始タイミングさえわかれば,光るタイミングはアプリ側で知っているので通信なぞ不要!ということです!

実際にやってみる

ちょっと自作してみたいと思います
今回やりたいことは特定の音に反応すればいいです
問題を簡単にすると,特定の周波数の音が聞こえたら〇〇するみたいなことができればいいわけです

geminiつくって〜
バイブコーディング万歳!

実際のプログラム
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Audio Reactive Background</title>
    <style>
        body {
            margin: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            background-color: #222; /* 初期カラー */
            color: white;
            font-family: sans-serif;
            transition: background-color 0.2s; /* 色の変化を少し滑らかに */
        }
        button {
            padding: 15px 30px;
            font-size: 1.2rem;
            cursor: pointer;
            z-index: 10;
        }
        #status {
            position: absolute;
            bottom: 20px;
        }
    </style>
</head>
<body>

    <button id="startBtn">マイク入力を開始してテスト</button>
    <div id="status"></div>

<script>
    const startBtn = document.getElementById('startBtn');
    const statusDiv = document.getElementById('status');
    let audioContext, analyser, microphone, dataArray;
    let isRunning = false;

    startBtn.addEventListener('click', async () => {
        if (isRunning) return;
        
        try {
            // 1. マイクの許可を求める
            const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
            
            // 2. オーディオコンテキストの作成
            audioContext = new (window.AudioContext || window.webkitAudioContext)();
            analyser = audioContext.createAnalyser();
            
            // 3. マイク入力を解析ノードに接続
            microphone = audioContext.createMediaStreamSource(stream);
            microphone.connect(analyser);

            // 4. 解析の設定 (FFTサイズ)
            analyser.fftSize = 512; 
            const bufferLength = analyser.frequencyBinCount;
            dataArray = new Uint8Array(bufferLength);

            startBtn.style.display = 'none';
            statusDiv.textContent = "音を鳴らしてみてください(口笛や拍手など)";
            isRunning = true;

            detectSound();

        } catch (err) {
            console.error(err);
            alert("マイクの利用が許可されませんでした。設定を確認してください。");
        }
    });

    function detectSound() {
        if (!isRunning) return;

        // 次の描画フレームを予約
        requestAnimationFrame(detectSound);

        // 周波数データを取得 (0〜255の範囲)
        analyser.getByteFrequencyData(dataArray);

        // --- ここで「特定のノイズ」を判定します ---
        
        // 例: 「特定の音域(高音)」に反応させる
        // dataArrayは低い音から高い音への配列になっています。
        // インデックスが大きいほど高音です。
        
        // 高音域の特定エリアの平均値を取ってみる (口笛などの検知)
        let targetFrequencySum = 0;
        let count = 0;
        // インデックス100〜150あたり(環境によりますが高音域)を監視
        for (let i = 100; i < 150; i++) {
            if (dataArray[i]) {
                targetFrequencySum += dataArray[i];
                count++;
            }
        }
        
        const average = count > 0 ? targetFrequencySum / count : 0;
        const threshold = 50; // 反応する感度(しきい値)

        if (average > threshold) {
            // しきい値を超えたら色を変える(ランダム色)
            const r = Math.floor(Math.random() * 255);
            const g = Math.floor(Math.random() * 255);
            const b = Math.floor(Math.random() * 255);
            document.body.style.backgroundColor = `rgb(${r},${g},${b})`;
            statusDiv.textContent = `検出! レベル: ${Math.floor(average)}`;
        } else {
            // 音が止んだら徐々に元の色に戻すなどの処理も可能
            // document.body.style.backgroundColor = "#222"; 
        }
    }
</script>
</body>
</html>

やっていることに関しては簡単で,ブラウザ標準のWeb Audio APIを使ってマイクから音を取得し,周波数を解析して,閾値超えてたら背景色変えるって感じです

背景を変えるだけじゃなく,

  • 画面でアニメーションを再生する
  • 〇秒後にライトを光らせる
    なんて機能を実装すればアプリの挙動を実現できそうですね!

最後に

自分で「これどうやって実現してるんだろう?」と色々考察してみるのは(実際に正しいかどうかは別にして)かなり面白い体験でした!

みなさんもぜひ「自分だったらどうやって実装するかな?」と考察してみてください!!!!

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?