何を作ったか
はじめに、何を作ったのかを書きます。
配信の企画で「誰がやるか」を決めるときに使う、参加者を決めるルーレットを作りました。名前をカンマで並べて書いておいて、ボタンを1つ押すと、その中の1つが大きく出ます。HTML のファイルが1枚と、その中に入れた JavaScript だけで動きます。外から部品を取ってくる書き方をしていませんので、開くときに何かが届いていないと動かない、という心配はいりません。
作った理由は、「次は誰の番か」を決める場面が、配信の企画にはよく出てくるからです。話し合いで決めると、決まらないまま時間が過ぎます。じゃんけんだと、画面の向こうにいる人には見えません。そこで、名前を入れておいてボタンを押すだけのものを用意しました。
同じことは、くじを引く、サイコロを振る、チャットの書き込みを数える、といったやり方でも決められます。それでも、名前をその場で足したり減らしたりできること、当たった人の名前が大きく出て、画面の向こうの人にも読めること、この2つは助かります。そこを狙って作りました。
中身は、見た目を決めるスタイル、名前を入れる欄、抽選するボタン、当たった人を出す欄、そして抽選の動きを書いた JavaScript です。書き上げた HTML は要素が21個で、閉じ忘れはありません。表示の確認は、OBS のブラウザソースに読み込んで行いました。
置くコード
作ったものをそのまま置きます。1枚のファイルです。名前を付けて保存し、OBS のブラウザソースに読み込ませると動きます。
<!DOCTYPE html>
<html lang='ja'>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<title>参加者を決めるルーレット</title>
<style>
:root {
--card: #14181f;
--line: #39424f;
--text: #f3f6fa;
--muted: #a8b3c1;
--accent: #ffd166;
--accent-dark: #2b2205;
}
* { box-sizing: border-box; }
body {
margin: 0;
padding: 32px 16px 48px;
background: #0a0d11;
color: var(--text);
font-family: 'Hiragino Kaku Gothic ProN', 'Yu Gothic', 'Noto Sans JP', 'Meiryo', sans-serif;
line-height: 1.8;
-webkit-font-smoothing: antialiased;
}
.card {
max-width: 760px;
margin: 0 auto;
background: var(--card);
border: 2px solid var(--line);
border-radius: 18px;
padding: 36px 32px 40px;
}
h1 {
margin: 0 0 10px;
font-size: 26px;
line-height: 1.4;
letter-spacing: 0.02em;
}
.lead {
margin: 0 0 28px;
font-size: 15px;
line-height: 1.8;
color: var(--muted);
}
.field-label {
display: block;
margin-bottom: 10px;
font-size: 16px;
color: var(--muted);
}
textarea {
display: block;
width: 100%;
min-height: 132px;
padding: 14px 16px;
background: #0a0d11;
color: var(--text);
border: 2px solid var(--line);
border-radius: 12px;
font-family: inherit;
font-size: 18px;
line-height: 1.8;
resize: vertical;
}
textarea:focus {
outline: none;
border-color: var(--accent);
}
.row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
margin-top: 18px;
flex-wrap: wrap;
}
.count {
margin: 0;
font-size: 16px;
color: var(--muted);
font-variant-numeric: tabular-nums;
}
.count strong {
margin: 0 4px;
font-size: 24px;
font-weight: 700;
color: var(--text);
}
button {
flex: 1 1 240px;
padding: 18px 32px;
background: var(--accent);
color: var(--accent-dark);
border: none;
border-radius: 12px;
font-family: inherit;
font-size: 22px;
font-weight: 700;
letter-spacing: 0.06em;
cursor: pointer;
}
button:hover:not(:disabled) { filter: brightness(1.08); }
button:disabled { opacity: 0.6; cursor: default; }
.result-box {
margin-top: 32px;
padding: 28px 24px 32px;
background: #0a0d11;
border: 2px solid var(--line);
border-radius: 14px;
text-align: center;
}
.result-label {
margin: 0 0 14px;
font-size: 16px;
color: var(--muted);
}
.result-name {
margin: 0;
min-height: 1.2em;
font-size: clamp(40px, 9vw, 76px);
font-weight: 700;
line-height: 1.2;
color: var(--accent);
word-break: break-word;
font-variant-numeric: tabular-nums;
}
.result-name.is-message {
font-size: clamp(18px, 4vw, 24px);
font-weight: 400;
line-height: 1.6;
color: var(--muted);
}
</style>
</head>
<body>
<main class='card'>
<h1>参加者を決めるルーレット</h1>
<p class='lead'>名前をカンマで区切って並べてください。ボタンを押すと、その中から1人が選ばれます。</p>
<label class='field-label' for='names'>参加者の名前(カンマ区切りで入力)</label><textarea id='names' rows='5'>あおい, かえで, さくら, つばさ, なお</textarea>
<div class='row'><p class='count' id='count'>参加者の人数:<strong>0</strong>名</p><button id='draw' type='button'>抽選する</button></div>
<div class='result-box'><p class='result-label'>選ばれた人:</p><p class='result-name is-message' id='result'>まだ抽選していません。ボタンを押してください。</p></div></main>
<script>(function () {var namesField = document.getElementById('names');var countText = document.getElementById('count');var drawButton = document.getElementById('draw');var resultText = document.getElementById('result');
function readNames() {return namesField.value.split(/[,、\uFF0C\n\r]+/).map(function (name) { return name.trim(); }).filter(function (name) { return name.length > 0; });}
function showCount() {var count = readNames().length;countText.innerHTML = '参加者の人数:<strong>' + count + '</strong>名';}
function setMessage(text) {resultText.textContent = text;resultText.classList.add('is-message');}
function setName(text) {resultText.textContent = text;resultText.classList.remove('is-message');}
function draw() {var names = readNames();
if (names.length === 0) {setMessage('参加者の名前が入っていません。名前をカンマで区切って入力してください。');return;}
if (names.length === 1) {setName(names[0]);return;}
var left = 18;drawButton.disabled = true;drawButton.textContent = '抽選中です';
var timerId = window.setInterval(function () {setName(names[Math.floor(Math.random() * names.length)]);left = left - 1;
if (left > 0) { return; }
window.clearInterval(timerId);setName(names[Math.floor(Math.random() * names.length)]);drawButton.disabled = false;drawButton.textContent = 'もう一度抽選する';}, 80);}
namesField.addEventListener('input', showCount);drawButton.addEventListener('click', draw);
showCount();
})();
</script>
</body>
</html>
名前を書き換える場所
名前は、<textarea id='names' rows='5'> の中に書いてあります。最初は「あおい, かえで, さくら, つばさ, なお」の5人です。ここを書き換えれば、そのまま自分の企画の名前になります。画面を開いてから入力欄を直接書き換えてもかまいません。書き換えると、下の「参加者の人数」もその場で数え直されます。
区切りはカンマだけではありません。JavaScript の readNames の中で、次の4つを区切りとして扱っています。
,半角のカンマ、全角の読点,全角のカンマ- 改行
ですので、1行に1人ずつ並べても読めます。スマートフォンから入れるときに、カンマを探さなくてすみます。前後の空白は落とし、何も書いていないところは数えません。最後にカンマを打っても、空の名前は入りません。
押したときの動き
ボタンを押すと、draw という function が動きます。順番は次のとおりです。
- 入力欄から名前を読み出します。
- 名前が0人のときは、当たった人の欄に「参加者の名前が入っていません。名前をカンマで区切って入力してください。」と出して終わります。
- 名前が1人のときは、抽選をせず、そのまま出します。1人で回すと、止まるまで待たされるだけだからです。
- 2人以上のときは、18回、80ミリ秒ごとに名前を入れ替えます。0.08秒 × 18回で、1.4秒ほどの間だけ名前が回ります。
- 回り終わったら、もう一度引いて、そこで止めます。止まったときの名前が当選です。
- 回っている間はボタンを押せないようにして、文字を「抽選中です」に変えます。二重に押して2回引いてしまうのを防ぐためです。
- 止まったら、ボタンが押せる状態に戻り、文字が「もう一度抽選する」に変わります。
回っている途中に見えている名前は、最後に出る人の手がかりにはなりません。止まる直前にもう一度引き直しているからです。画面の前にいる人が、出た名前を予想できないようにしてあります。
当たった名前は result-name のところに出ます。文字の大きさは clamp(40px, 9vw, 76px) と書いてあります。画面の幅が狭いときは小さく、広いときは大きくなりますが、40ピクセルより小さくはなりません。配信の画面に重ねたときに読める大きさを残すためです。長い名前が入っても、word-break: break-word で折り返します。
ブラウザで開いたときの画面
ここで1つ、正直に書いておきます。ブラウザで単体で開いた画面は、わたくしは撮っていません。表示の確認は、OBS のブラウザソースに読み込んで行いました。撮っていない画面をここに置くと、読む人に違うことを伝えてしまいますので、置きません。
その代わりに、開いたときに何が並ぶかを、コードのとおりに書いておきます。上から順に、次の6つです。
- 見出しの「参加者を決めるルーレット」
- 説明の一行「名前をカンマで区切って並べてください。ボタンを押すと、その中から1人が選ばれます。」
- 「参加者の名前(カンマ区切りで入力)」という見出しと、名前の入った入力欄
- 「参加者の人数:5名」という数え方の表示
- 黄色い「抽選する」というボタン
- いちばん下に「選ばれた人:」と、まだ引いていないときの案内
背景は濃い色で、文字は白、当たった人の名前とボタンだけが黄色です。当たった名前は、いちばん下の枠の中に大きく出ます。
OBS のブラウザソースに置いて動かしてみた画面
次に、OBS のブラウザソースにこのファイルを読み込ませて動かしたことを書きます。手順は次のとおりです。
- ソースの一覧で、新規作成を選び、ブラウザを選びます。名前は分かりやすいものでかまいません。
- 「ローカルファイル」に印を付けて、参照のボタンから、保存した HTML のファイルを選びます。
- 幅と高さを決めます。この欄は、自分の配信の画面に合わせて決めてください。わたくしは、用意したカードが切れずに収まる大きさにしました。
- 「カスタム CSS」という欄もあります。見た目は HTML の中のスタイルで決まるように書いていますので、この欄を使わなくても表示できました。
- OK を押すと、配信の画面の上に重なって出ます。
読み込む前に、設定している途中の画面を撮りました。読み込む場所、幅と高さ、カスタム CSS の欄が写っています。

読み込んだあと、実際に映った画面を撮りました。

映った画面では、名前の入った入力欄と人数が出て、ボタンを押すと1つが大きく出ました。ボタンの文字は、押している間だけ「抽選中です」に変わります。名前が大きく出ますので、配信の画面に重ねても読めます。
ここで、はっきり書いておきたいことがあります。わたくしは配信をしていません。確かめたのは、自分の OBS の中に読み込んで、そこで押したところまでです。「配信で使ってみたら、こうだった」ということは書けません。実際の配信の画面で、見ている人にどう見えたかは、わたくしは見ていません。使うときは、自分の画面で一度出して、文字が読めるかどうかを確かめてください。
こういう使い方はいかがですか
同じファイルが、3通りの場面で使えます。名前の書き方と、押す回数が変わるだけです。わたくしがその場面で実際に使ったわけではありませんので、使い方の案として読んでください。
順番を決める
参加者の名前をその人数だけ並べて、1人ずつ引きます。当たった人から順番に、という決め方です。このときは、一度出た人を消していく必要があります。消し方は、このあとの「こう広げられます」のところに書きます。
見え方としては、名前が大きく出て、下のボタンが「もう一度抽選する」に変わります。そのまま押せば次を引けますので、順番が決まるまで続けられます。何人ぶん引いたかを数えておかないと分からなくなりますので、決まった順番はどこかに書き留めておくといいと思います。
罰ゲームを割り当てる
罰ゲームの内容を名前の欄に入れて引く、という使い方です。「ものまね」「一発芸」のように書いても、そのまま大きく出ます。人の名前を出すのではなく、内容を出すだけの違いです。
このときは、当たった人が誰かは別に決めておくことになります。1回の抽選では、人と内容の両方は決められません。人を引いてから内容を引く、という順番で2回押す使い方もあります。ただし、2つの結果が同じ画面に残るわけではありませんので、どちらがどちらだったかを取り違えないようにしてください。
視聴者へのプレゼントの当選者を決める
応募してくれた人の名前を並べて引く使い方です。このときは、参加者が多いほど、名前を並べるのに手間がかかります。カンマでも、読点でも、改行でも区切れますので、応募の一覧をそのまま貼り付けて、区切りだけ整えると早いと思います。
気をつけるところが2つあります。1つは、このファイルは引いたあとに名前を消しませんので、同じ人が2回出ることがあるという点です。もう1つは、引いた結果の記録が残らないという点です。画面を閉じると消えますので、必要なら別に控えてください。どちらも、次の書き換えで変えられます。
こう広げられます:手を入れる所
ここからは、手を入れる場所を書きます。どの行を変えると何が変わるか、という形で並べます。
名前を増やす
入力欄の <textarea id='names' rows='5'> の中身を増やすだけです。仕組みの上では人数の上限はありませんが、大人数のときの動きは、わたくしは試していません。画面の高さが足りなくなるときは、rows='5' の数字を増やしてください。
一度出た人を消して、重複をなくす
今のコードは、引いたあとも名前を残します。消したいときは、抽選が終わったところで、当たった名前を入力欄から取り除きます。今は最後のところが setName(names[Math.floor(Math.random() * names.length)]); の1行になっています。その行を、次の形に置き換えます。
var picked = names[Math.floor(Math.random() * names.length)];
setName(picked);
// 当たった人を入力欄から消す
namesField.value = names
.filter(function (n) { return n !== picked; })
.join(', ');
showCount();
この置き換えは、わたくしは試していません。仕組みとしては、当たった名前と同じものを外して、残りをカンマでつなぎ直し、人数を数え直す、という流れです。同じ名前が2つ入っていると、その両方が消えます。1人ずつにしたいときは、名前を重ねずに入れてください。残りが0人になると、次に押したときは「参加者の名前が入っていません」と出ます。そこまでいく前に止めたいときは、残りの人数を見てボタンを押せなくする行を足すことになります。
音や色を足す
色は、スタイルの上のほうにある --accent: #ffd166; を変えると、ボタンと当たった名前の色が一緒に変わります。--accent-dark: #2b2205; はボタンの文字の色です。背景は --card: #14181f; と、body の background: #0a0d11; で決まっています。ここを変えると、配信の画面の色に合わせられます。
音は、わたくしは試していません。試していないことを、試したように書かないでおきます。考え方としては、音のファイルを HTML と同じ場所に置いて、止まったところで鳴らす1行を足します。
// 同じ場所に置いた音のファイルを鳴らす
new Audio('chime.mp3').play();
この書き方も、わたくしの手元では動かしていません。音のファイルも用意していません。使うときは、自分の画面で確かめてください。
回る速さと回数を変える
80 は、名前を入れ替える間の時間です。数字を小さくすると速く回り、大きくするとゆっくり回ります。18 は回数です。増やすと長く回ります。80 と 18 を掛けた分だけ待つことになりますので、今は1.4秒ほどです。長くしすぎると、待たされている感じが出ます。
文字の大きさを変える
当たった名前の大きさは clamp(40px, 9vw, 76px) の3つの数字で決まります。左から、これより小さくしない大きさ、画面の幅に対する割合、これより大きくしない大きさです。配信の画面が大きいときは、右の数字を上げてください。
確かめたのはどこまでか
わたくしが確かめたのは、自分の OBS に読み込んで、そこで押したところまでです。次のことは確かめていません。
- 実際の配信の画面で、見ている人側にどう見えるか
- 音を鳴らす書き方
- 一度出た人を消す書き換え
- 大人数のときの動き
これは、わたくしが配信をしていないからです。ですので、この記事には「配信で使ってみたら」という話はありません。使う前に、自分の画面で一度出して、文字が読めるかどうかを確かめてください。
借りたものの出どころ
最後に、借りたものの出どころを書きます。
自分で書いたもの
この記事に載せた HTML、CSS、JavaScript は、わたくしが書いたものです。他所のコードを写したところはありません。記事の中の文章も、わたくしが書きました。
OBS Studio
ブラウザソースは、OBS Studio という道具の中の機能です。作っているのは OBS Project です。
- 公式サイト: https://obsproject.com/
- GitHub: https://github.com/obsproject/obs-studio
作者の X の在りかは、わたくしが確かめられませんでした。分からないものを推測で書くと、読む人が迷いますので、ここには書きません。
Math.random
抽選に使っている命令は、JavaScript に元から入っているものです。使い方の説明は MDN のページにあります。
- https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Math/random
書体
スタイルの中に、’Hiragino Kaku Gothic ProN’、’Yu Gothic’、’Noto Sans JP’、’Meiryo’ の4つを並べました。左から順に、その機械に入っているものが使われます。書体のファイルは同梱していませんので、置き場所は挙げません。このうち Noto Sans JP は Google が作っている書体で、配布元は Google Fonts です。
- https://fonts.google.com/
参考にした記事
今回、第三者の記事を参考にはしていません。書いたのは、手元で動かして見たことと、そのコードの中身です。参考にしたと書ける記事が無いのに、それらしい名前を並べるのはやめておきます。
確認した範囲
動かした結果として書いたのは、OBS のブラウザソースに読み込んで映った画面と、そのときに押して名前が出たことだけです。それ以外のことは書いていません。ここまで読んでくださって、ありがとうございました。


コメント