マガジン

操作音の長さを10ms刻みで変える:短すぎる音と長すぎる音

遊びをつくる

執筆にClaudeを使用しています。

雨の窓辺にあるアップライトピアノと小さなリズムマシン。
記事のテーマをもとに制作したAI生成イメージ。実際のゲーム画面ではありません。

ボタンを押した瞬間に鳴る音が、5msだけ長かったら気づくでしょうか。逆に、100msの音を300msに伸ばしたとき、操作の軽快さはどこから損なわれるのでしょうか。ゲームの操作音は、音程や音色の話に比べて「長さ」が後回しにされがちです。しかし長さは、音が「操作に付いてくる」と感じるか、「操作の後ろでまだ鳴っている」と感じるかを左右しそうな要素です。 この記事は結果の報告ではなく、その答えを自分の耳と手で出すための実験設計書です。数字の目安は書きません。環境が違えば答えが変わるからです。代わりに、ブラウザだけで動く約40行の試作、記録の取り方、結果の読み方、誤りやすい点を順に示します。 問いは二つに分けておきます。 短すぎる失敗と長すぎる失敗は別の現象です。一つの評価軸にまとめず、別々に記録します。

この記事の目次
  1. 準備するもの
  2. 試作:長さをスライダーで変える
  3. 手順:粗く当たりをつけ、10ms刻みで絞る
  4. 結果の読み方
  5. 誤りやすい点
  6. 次の行動

準備するもの

必要なのは次の4つです。

  • 音の出るパソコンと、HTMLを保存できるテキストエディタ
  • 有線のイヤホンかヘッドホン。無線は伝送の遅れが加わり、「軽快さ」の評価に混ざります。
  • 静かな部屋と、固定した音量。実験の途中でOSの音量を触らないようにします。
  • 紙かスプレッドシート。評価を書き留めるために使います。

固定する条件も先に決めます。音の高さ(この試作では660Hzの正弦波)、音量、押す側のボタン、評価者の姿勢です。変えてよいのは長さだけです。

「軽快」の意味も、試す前に一文で書いておきます。たとえば「押した指の動きと音が一体に感じられ、次の操作に気が散らない状態」のように、自分の言葉で定義します。後で基準がぶれるのを防ぐためです。

試作:長さをスライダーで変える

次のコードを onset.html として保存し、ブラウザで開きます。外部ライブラリは使わず、サーバーも不要です。

html
<!doctype html>
<meta charset="utf-8">
<button id="b" style="font-size:2em">押す</button>
<p>長さ <input id="r" type="range" min="10" max="400" step="10" value="100"> <span id="v">100</span>ms</p>
<p><button id="t">ブラインド試行</button> <span id="s"></span></p>
<pre id="log"></pre>
<script>
let ctx, ms = 100, blind = false, order = [], n = 0, cur = 0, rows = [];
const $ = id => document.getElementById(id);
function beep(len) {
  ctx = ctx || new AudioContext();
  const t = ctx.currentTime, d = len / 1000;
  const o = ctx.createOscillator(), g = ctx.createGain();
  o.frequency.value = 660;
  g.gain.setValueAtTime(0, t);
  g.gain.linearRampToValueAtTime(0.2, t + 0.003);
  g.gain.setValueAtTime(0.2, t + d - 0.005);
  g.gain.linearRampToValueAtTime(0, t + d);
  o.connect(g).connect(ctx.destination);
  o.start(t); o.stop(t + d + 0.01);
}
$('r').oninput = e => { ms = +e.target.value; $('v').textContent = ms; };
$('b').onclick = () => { cur = blind ? order[n] : 0; beep(blind ? cur : ms); };
$('t').onclick = () => {
  const L = [30, 60, 100, 150, 220, 300];
  order = [...L, ...L, ...L];
  for (let i = order.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [order[i], order[j]] = [order[j], order[i]];
  }
  blind = true; n = 0; rows = [];
  $('s').textContent = '1/' + order.length + ':数回押して、1〜5で評価';
};
document.onkeydown = e => {
  if (!blind || !cur || !/^[1-5]$/.test(e.key)) return;
  rows.push(cur + ',' + e.key); cur = 0; n++;
  if (n >= order.length) {
    blind = false;
    $('s').textContent = '終了';
    $('log').textContent = 'ms,score\n' + rows.join('\n');
  } else $('s').textContent = (n + 1) + '/' + order.length;
};
</script>

仕組みは単純です。beep は指定した長さだけ正弦波を鳴らします。音量は先頭3msで立ち上げ、末尾5msで下げます。この数ミリ秒のなだらかな変化がないと、音の始まりと終わりで波形が急に切れ、プチッというノイズが混ざります。そのノイズは「短い音の印象」を左右するため、長さの比較から外すために入れてあります。

また、beep は鳴るたびに独立した音を作ります。そのため、前の音が鳴り終わる前にもう一度押すと音が重なります。問いBはこの性質を利用します。

ブラウザは、ユーザーが操作するまで音の再生を許可しないのが普通です。このコードは最初の「押す」ボタンで初めて AudioContext を作るので、その制限に引っかかりません。

手順:粗く当たりをつけ、10ms刻みで絞る

最初から10ms刻みで30段階を総当たりすると、評価の回数が増えて耳が疲れます。二段階に分けます。

第1段階:スライダーで自由に聞く

非ブラインドの状態で、スライダーを10msから400msまで動かしながら、普通に押して聞き比べます。ここでの目的は、評価が変わりそうな区間の見当をつけることです。次の2点をメモします。

  • 「音というより、クリックに聞こえる」と感じ始める長さ
  • 「押し終わったのに鳴っている」と感じ始める長さ

第2段階:ブラインド試行で評価する

「ブラインド試行」を押すと、6種類の長さ(30、60、100、150、220、300ms)が3回ずつ、計18試行でランダムに並びます。流れは次のとおりです。

  1. 「押す」ボタンを一度クリックして、フォーカスを移します。これを忘れると、スペースキーが「ブラインド試行」ボタンを再び押してしまいます。
  2. 音の長さは画面に出ません。スライダーは無視します。
  3. 自分が普段押す速さで、3〜4回続けて押します。
  4. 数字キー1〜5で軽快さを評価します。5が最も軽快、1が最も重い、とあらかじめ決めておきます。
  5. 18試行が終わると、画面下に ms,score の表が出ます。そのままコピーして保存します。

評価は1試行につき1つです。同じ試行で何度も付け直さないでください。

第3段階:10ms刻みで絞り込む

粗い結果で評価が大きく動いた隣り合う2点を選びます。たとえば60msと100msの間で評価が変わったなら、コードの L を [60, 70, 80, 90, 100] に書き換えて同じ手順を繰り返します。長い側の変化点も同じ方法で絞ります。

記録の単位は「長さごとに3回以上」を守ります。余裕があれば別の日にもう一度行い、同じ長さの評価が日によってどれだけ揺れるかも見ます。この揺れが、10ms刻みの差を読めるかどうかの目安になります。

連打の間隔を別に記録する

問いBでは、連打の間隔も記録します。音の長さが押す間隔より長ければ、音は必ず重なります。たとえば約200msおきに押す人が220msの音を使えば、毎回少しずつ重なります。自分の連打の間隔を、ストップウォッチ代わりのメトロノームアプリなどで大まかに測ってメモしておくと、後で「重なり始め」と「濁り」を結びつけて読めます。

結果の読み方

長さごとに評価の平均か中央値を出し、折れ線にします。形によって次のように読みます。

  • 短い側だけ下がる、読み方:短すぎる側に問題がある、次にすること:下がり始める長さの少し上を下限にします
  • 長い側だけ下がる、読み方:長すぎる側に問題がある、次にすること:連打の間隔と比べ、重なりが原因かを確かめます
  • 両側が下がる山型、読み方:快適な範囲がある、次にすること:山の頂上付近を候補にします
  • ほぼ平らで揺れも大きい、読み方:長さは主要因ではない、次にすること:音色、音量、立ち上がりを疑います

長い側が下がる場合、それが「音の長さそのもの」の問題なのか、「連打時の重なり」の問題なのかは、グラフだけでは分かりません。一回だけ鳴らす非ブラインドの聞き比べ(第1段階)で長い音を押してみてください。単発では気にならず、連打で急に評価が下がるなら、原因は重なりです。その場合の対策は、音を短くするだけとは限りません。次の音で前の音を止める、あるいは余韻を絞るといった方法もあります。

短い側が下がる場合は、「音が短い」ことより「クリックにしか聞こえない」ことが原因の可能性があります。一般に、極端に短い音は音程が分かりにくくなり、実際より小さく聞こえやすいとされます。これは長さだけの問題ではなく、音量の感じ方も絡んでいます。この切り分けは次の節で扱います。

評価の差が小さいときは、判断を急がないでください。同じ長さを繰り返した評価の揺れ(前述の別日の記録)と比べ、揺れの範囲に収まる差は「差がない」と読むほうが安全です。

誤りやすい点

音量の感じ方が長さに引きずられる。 振幅(波の大きさ)を同じにしても、短い音は小さく聞こえがちです。「短い音が低評価」の理由が、長さではなく単に小さいからという可能性があります。気になるなら、短い側だけ 0.2 を少し上げて再度評価し、結果が変わるかを見ます。ただし、音量を触ったらその条件は別の実験として記録を分けます。

立ち上がりと立ち下がりのなだらかさが印象を変える。 試作の3msと5msを0にすると、ノイズが増えます。逆に長くしすぎると、10msの音ではほとんど平坦な部分が残りません。この値を変えるなら、変えた版と変えない版で記録を分けます。

順番の影響。 長さを短い順や長い順に聞くと、直前の音との比較で評価が決まります。ブラインド試行がランダムなのはこのためです。手動で並べ替える場合も、順番を固定しないでください。

聞き慣れ。 何十回も続けると、耳も指も疲れて評価基準が変わります。18試行ごとに休憩を挟み、同じ日に何セットも行わないようにします。

環境の違い。 スピーカーとイヤホン、OSの出力設定、ブラウザの違いで、音が鳴り出すまでの遅れが変わります。この実験は「自分の環境での軽快さ」を測るものです。そこで出た数値を、他の人の環境にもそのまま当てはめないでください。可能なら、別の端末やほかの人にも同じ手順で試してもらい、結果が重なる範囲を探します。

画面の動きとの組み合わせ。 この試作は音だけを扱います。実際のゲームでは、ボタンの沈み込みやエフェクトなどの視覚が同時に動くため、音の長さの印象も変わりえます。音単体で答えを出した後に、視覚と組み合わせて再評価する、という順に進めると混乱しません。

次の行動

まず試作を保存し、第1段階のスライダー聞き比べだけを5分行ってみてください。「短すぎる」と「長すぎる」の境目が自分の中で見えたら、第2段階のブラインド試行に進みます。

比較の素材が欲しいときは、アソバースの「雨音のピアノ」で余韻の切り替えを試すと、音の残り方の違いを聞き取る練習になります。余韻の長さが演奏の軽快さにどう影響するかは、作品を開いて確かめてください。この試作は単独のHTMLなので、そのまま作品として投稿して、他の人に長さを聞き比べてもらう使い方もできます。