配信の待機画面づくりに、画像生成AIは使えるのか。自分で試してみました

obsUncategorized
スポンサーリンク
スポンサーリンク

はじめに:配信の待機画面づくりに、画像生成AIを試した理由

配信の待機画面というのは、配信が始まるまでの間、画面に出ている絵と文字のことです。開始を待っている人が見るものですから、動きは要りません。要るのは、一枚の絵と、いくつかの短い言葉だけです。

その一枚の絵をどうするか。自分で描く、写真を使う、素材を借りる。そのほかに、画像生成AIに作ってもらうという手があります。待機画面は毎回同じものを使いますので、一度用意すれば当分それで済みます。急ぎで一枚要るときには、助かりそうです。

では、待機画面に置ける絵は、本当に出てくるのか。人の話を聞くより早いと思いまして、自分の手元で試してみることにしました。

先に、書けないことを書いておきます。わたくしは配信をしていませんので、「配信で使ってみたらこうだった」ということは書けません。確かめたのは、待機画面を一枚作るときの手順と、そこで出てきた絵が1920×1080の画面でどうなるか、そしてそれを縮めたときの見え方までです。実際に見た人がどう思ったかは、分かりません。そのつもりで読んでいただけると助かります。

先に決めたこと:待機画面のどこに、どんな絵を置くか

作り始める前に、決めることを決めました。決めずに手を動かし始めると、あとで文字の位置を動かすたびに、絵のほうも作り直しになります。

待機画面の大きさは1920×1080としました。配信でよく使われている横長の大きさです。絵は画面いっぱいに敷きます。周りに余白を残すと、配信サイトの一覧に並んだときに小さく見えるためです。

文字は絵の上に重ねます。置き場所は中央、上から520pxと660pxのところ。上の行に「現在時刻:」と時刻、下の行に「配信時間:」と開始予定の時刻を置きます。

画面に出す言葉は略さない、と決めました。「いま」「配信」ではなく「現在時刻:」「配信時間:」と書きます。初めて見た人が読んで分かる言葉でないと、待機画面にしている意味がありません。これは画面に出すものを作るときの、わたくしの決めごとです。

大きさには差をつけます。時刻の数字は160px、その前の「現在時刻:」は60px。下の行はもっと小さく、62pxと40pxにしました。同じ大きさで並べますと、どこから読めばいいのか分からなくなります。

時刻の数字は等幅の書体で出すことにしました。1分ごとに数字が変わりますので、幅が変わると文字の位置が横に動いてしまいます。

文字を重ねるということは、下の絵の上に文字を置くということです。絵が明るいところでは、白い文字が沈みます。そのために、文字には影と細い縁取りを付け、絵全体を暗くする量をあとから調整できるようにしました。三つとも、あとから足すのではなく、最初から入れておくつもりで決めています。

試した手順1:生成画面に同じ指示を入れ直して、何度か作る

使った画像生成AIの名前は、ここでは出していません。道具の版が変わると出てくる絵も変わりますので、名前を挙げても、読む方が同じ絵を得られるとは限らないためです。わたくしが試したのは、ブラウザで指示を入力しますと絵が出てくる形式のものです。

指示は、一度で決めませんでした。同じ指示をそのまま入れ直して、何度も出しました。同じ指示でも、毎回まったく同じ絵にはなりません。そこが絵筆と違うところで、わたくしには都合がよかったです。一枚がしっくりこなくても、入れ直せば別の一枚が出てきます。

指示に入れる条件は、待機画面に敷くことを前提に決めました。

  • 横長にすること。中央と上のほうは静かにしておくこと。文字を重ねるためです。
  • はっきりした文字やロゴ、大きな顔を入れないこと。目がそちらに行ってしまいます。
  • 色を強くしすぎないこと。白い文字を重ねたときに、色がぶつかります。
  • 全体を、明るくもしすぎず、暗くもしすぎないこと。

出てきた絵は、並べて見比べました。見ているのは、絵として好きかどうかではありません。文字を重ねたときに読めるかどうかです。明るい部分が中央にある絵は、文字の後ろを暗くしても、少し濁って見えました。

何枚か出して、暗い場所が広く、中央に大きなものが無い絵を選びました。選ぶのに使った時間は、絵を一枚描くよりずっと短かったです。

試した手順2:出てきた絵を1920×1080に敷いて、文言を重ねる

選んだ絵を待機画面の大きさに敷いて、その上に文字を重ねます。ここから先は画像生成AIではなく、自分で書いたコードで行いました。絵を出すところは生成画面、敷いて文字を重ねるところは手元のコード、という分け方です。

書いたのは、一枚のHTMLファイルと、その中のJavaScriptです。次のことができます。

  • 選んだ絵を1920×1080に敷く
  • 背景を暗くする量を、スライダーで変える
  • 「現在時刻:」「配信時間:」の文字を重ねる
  • 仕上がりをPNGで書き出す
  • 480×270に縮めた見え方を、同じ画面の中に並べる

最後の一つが、いちばん確かめたかったところです。待機画面は、配信サイトの一覧やスマートフォンでは、原寸のままでは出ません。縮んで出ます。原寸で読めても、縮んで読めなければ用を足しません。

コードはこれです。そのまま載せます。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>画像生成AIの絵に待機画面の文字を重ねて、縮めたときに読めるか確かめる</title>
<style>
  :root {
    --paper: #f5f2ec;
    --ink: #23202a;
    --sub: #6d6672;
    --line: #ded7cb;
  }
  * { box-sizing: border-box; }
  body {
    margin: 0;
    padding: 40px 20px 80px;
    background: var(--paper);
    color: var(--ink);
    font-family: "Hiragino Sans", "Noto Sans JP", "Yu Gothic", Meiryo, sans-serif;
    line-height: 1.9;
  }
  .wrap { max-width: 1040px; margin: 0 auto; }
  h1 { margin: 0 0 10px; font-size: 25px; line-height: 1.6; }
  .lead { margin: 0 0 36px; color: var(--sub); font-size: 15px; }
  section { margin-bottom: 44px; }
  h2 {
    margin: 0 0 14px;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--line);
    font-size: 14px;
    font-weight: 600;
    letter-spacing: 0.1em;
    color: var(--sub);
  }
  .controls { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 18px; }
  .hint { font-size: 13px; color: var(--sub); }
  .filebtn {
    display: inline-block;
    padding: 9px 18px;
    border: 1px solid var(--ink);
    border-radius: 999px;
    background: #fff;
    font-size: 14px;
    cursor: pointer;
  }
  .filebtn input { position: absolute; width: 1px; height: 1px; opacity: 0; }
  .picks { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 16px; }
  .pick {
    padding: 8px 16px;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: #fff;
    color: var(--ink);
    font: inherit;
    font-size: 13px;
    cursor: pointer;
  }
  .pick.on { background: var(--ink); border-color: var(--ink); color: #fff; }
  input[type="range"] { width: 260px; accent-color: #9c3b2b; }
  output { min-width: 3.5em; font-size: 14px; font-variant-numeric: tabular-nums; }
  canvas { display: block; border-radius: 4px; background: #000; }
  #screen { width: 100%; height: auto; }
  #small { width: 480px; height: auto; max-width: 100%; }
  .cap { margin: 10px 0 0; font-size: 13px; color: var(--sub); line-height: 1.7; }
  .savebtn {
    margin-top: 16px;
    padding: 9px 18px;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: #fff;
    color: var(--ink);
    font: inherit;
    font-size: 14px;
    cursor: pointer;
  }
</style>
</head>
<body>
<div class="wrap">

<h1>画像生成AIの絵に、待機画面の文字を重ねてみる</h1><p class="lead">同じ指示で何度か作った絵をここに置き、1920×1080の待機画面に敷いて文字を重ねます。最後に小さく表示したときの見え方を並べて、読めるかどうかを目で確かめます。</p>

<section><h2>1.敷く絵を選ぶ</h2><div class="controls"><label class="filebtn">自分の絵を選ぶ<input type="file" id="files" accept="image/*" multiple></label><span class="hint">選ばないときは、明るい絵と暗い絵の見本で試せます。</span></div><div class="picks" id="sourceList"></div></section>

<section><h2>2.背景を暗くする量を決める</h2><div class="controls"><input type="range" id="dim" min="0" max="80" value="45" aria-label="背景を暗くする量"><output id="dimOut">45%</output><span class="hint">明るい絵のときは、文字の後ろを多めに暗くします。</span></div></section>

<section><h2>3.1920×1080の待機画面</h2><canvas id="screen" width="1920" height="1080"></canvas><p class="cap">原寸は1920×1080です。ここでは横幅に合わせて縮めて表示しています。</p><button type="button" class="savebtn" id="save">この待機画面をPNGで書き出す</button></section>

<section><h2>4.縮小したときの見え方</h2><canvas id="small" width="480" height="270"></canvas><p class="cap">480×270を原寸で並べています。配信サイトの一覧や、スマートフォンで見たときの大きさの目安です。</p></section>

</div>

<script>const W = 1920;const H = 1080;

// 画面に出す言葉は略さない。「いま」「配信」ではなく「現在時刻:」「配信時間:」と書く。const TEXT = {clockLabel: '現在時刻:',startLabel: '配信時間:',startValue: '20:00'};

// 見た目の決めごと。余白・行間・大きさの差は、先にここで決めてから描く。const LOOK = {dim: 0.45,clockLabelSize: 60,clockSize: 160,startLabelSize: 40,startSize: 62,clockBaseline: 520,startBaseline: 660,shadowBlur: 26,outlineWidth: 5};

const FONT_JP = '"Hiragino Sans", "Noto Sans JP", "Yu Gothic", Meiryo, sans-serif';// 時刻の数字は等幅にする。1分ごとに数字が変わっても、桁が横に動かない。const FONT_NUM = '"Courier New", Consolas, ui-monospace, monospace';

const fontOf = (weight, size, family) => weight + ' ' + size + 'px ' + family;

const screen = document.getElementById('screen');const small = document.getElementById('small');const sourceList = document.getElementById('sourceList');const fileInput = document.getElementById('files');const dimInput = document.getElementById('dim');const dimOut = document.getElementById('dimOut');

let sources = [];let current = 0;let dim = LOOK.dim;

// ── 絵を敷く ─────────────────────────────

function drawCover(ctx, image) {const iw = image.naturalWidth || image.width;const ih = image.naturalHeight || image.height;const scale = Math.max(W / iw, H / ih);const dw = iw * scale;const dh = ih * scale;ctx.drawImage(image, (W - dw) / 2, (H - dh) / 2, dw, dh);}

// ── 文字を重ねる ─────────────────────────

function paintText(ctx, text, x, y, fontSpec, fill) {ctx.font = fontSpec;ctx.textAlign = 'left';ctx.textBaseline = 'alphabetic';

// 影を先に落として、明るい絵の上でも字が浮くようにするctx.save();ctx.shadowColor = 'rgba(0, 0, 0, 0.62)';ctx.shadowBlur = LOOK.shadowBlur;ctx.shadowOffsetY = 6;ctx.fillStyle = fill;ctx.fillText(text, x, y);ctx.restore();

// 明るい部分に重なったときのために、細い縁取りを足すctx.save();ctx.lineJoin = 'round';ctx.lineWidth = LOOK.outlineWidth;ctx.strokeStyle = 'rgba(0, 0, 0, 0.38)';ctx.strokeText(text, x, y);ctx.restore();}

function drawLine(ctx, label, value, labelFont, valueFont, baseline, fill) {ctx.font = labelFont;const labelW = ctx.measureText(label).width;ctx.font = valueFont;const valueW = ctx.measureText(value).width;

const left = Math.round((W - (labelW + valueW)) / 2);paintText(ctx, label, left, baseline, labelFont, fill);paintText(ctx, value, left + labelW, baseline, valueFont, fill);}

function drawWords(ctx, clockText) {drawLine(ctx, TEXT.clockLabel, clockText,fontOf(500, LOOK.clockLabelSize, FONT_JP),fontOf(700, LOOK.clockSize, FONT_NUM),LOOK.clockBaseline, '#ffffff');drawLine(ctx, TEXT.startLabel, TEXT.startValue,fontOf(500, LOOK.startLabelSize, FONT_JP),fontOf(700, LOOK.startSize, FONT_NUM),LOOK.startBaseline, 'rgba(255, 255, 255, 0.94)');}

// ── 仕上げ ──────────────────────────────

function nowText() {const d = new Date();const hh = String(d.getHours()).padStart(2, '0');const mm = String(d.getMinutes()).padStart(2, '0');return hh + ':' + mm;}

function draw() {const source = sources[current];if (!source) return;

const ctx = screen.getContext('2d');ctx.clearRect(0, 0, W, H);drawCover(ctx, source.image);

// 明るい絵の上でも文字が沈まないように、背景を一段だけ暗くするctx.fillStyle = 'rgba(11, 10, 14, ' + dim + ')';ctx.fillRect(0, 0, W, H);

drawWords(ctx, nowText());

// 縮めたときの見え方を、原寸(480×270)で並べるconst sctx = small.getContext('2d');sctx.clearRect(0, 0, 480, 270);sctx.drawImage(screen, 0, 0, 480, 270);}

// ── 見本の絵(自分の絵を選ぶまでの仮)─────────

function makeDemo(paint) {const c = document.createElement('canvas');c.width = W;c.height = H;paint(c.getContext('2d'));return c;}

const demoSources = [{name: '明るい絵の見本',image: makeDemo((g) => {const grad = g.createLinearGradient(0, 0, W, H);grad.addColorStop(0, '#ffe8c6');grad.addColorStop(0.55, '#ffc79b');grad.addColorStop(1, '#f3a9c0');g.fillStyle = grad;g.fillRect(0, 0, W, H);for (let i = 0; i < 8; i += 1) {g.beginPath();g.arc(220 + i * 220, 1000 - i * 80, 150 + i * 18, 0, Math.PI * 2);g.fillStyle = 'rgba(255, 255, 255, 0.15)';g.fill();}})},{name: '暗い絵の見本',image: makeDemo((g) => {const grad = g.createLinearGradient(0, 0, 0, H);grad.addColorStop(0, '#131a2b');grad.addColorStop(0.6, '#242c46');grad.addColorStop(1, '#3b2440');g.fillStyle = grad;g.fillRect(0, 0, W, H);for (let i = 0; i < 10; i += 1) {g.beginPath();g.arc(160 + i * 200, 200 + (i % 3) * 260, 90 + i * 12, 0, Math.PI * 2);g.fillStyle = 'rgba(160, 190, 255, 0.07)';g.fill();}})}];

// ── 画面の操作 ───────────────────────────

function renderPicks() {sourceList.textContent = '';sources.forEach((source, index) => {const button = document.createElement('button');button.type = 'button';button.className = index === current ? 'pick on' : 'pick';button.textContent = (index + 1) + '.' + source.name;button.addEventListener('click', () => {current = index;renderPicks();draw();});sourceList.appendChild(button);});}

function applyLoaded(loaded) {const picked = loaded.filter((item) => item !== null);if (picked.length === 0) return;sources = picked;current = 0;renderPicks();draw();}

fileInput.addEventListener('change', () => {const files = Array.from(fileInput.files || []);if (files.length === 0) return;

const loaded = new Array(files.length).fill(null);let left = files.length;

files.forEach((file, index) => {const image = new Image();const done = () => {left -= 1;if (left === 0) applyLoaded(loaded);};image.onload = () => {const base = file.name.replace(/\.[^.]+$/, '');loaded[index] = { name: base || ('絵 ' + (index + 1)), image: image };done();};image.onerror = done;image.src = URL.createObjectURL(file);});});

dimInput.addEventListener('input', () => {dim = Number(dimInput.value) / 100;dimOut.textContent = dimInput.value + '%';draw();});

document.getElementById('save').addEventListener('click', () => {const link = document.createElement('a');link.download = 'waiting-screen-1920x1080.png';link.href = screen.toDataURL('image/png');link.click();});

// ── はじめの一歩 ─────────────────────────

sources = demoSources;
renderPicks();
draw();
setInterval(draw, 1000);
</script>
</body>
</html>

はじめに開いたときは、まだ自分の絵がありませんので、明るい絵と暗い絵の見本が2つ出ます。見本はコードの中で描いています。自分の絵を選びますと、そちらに切り替わります。

「現在時刻:」と略さずに書いているところ、数字だけ等幅の書体にしているところ、文字に影と縁取りを足しているところ。どれも、先に決めたことに対応しています。あとから足しますと、全体のつり合いが崩れます。先に決めて、先に書きました。

このファイルを OBS のブラウザソースに読み込んで、映った画面を撮りました。

OBSのブラウザソースに読み込んだ待機画面

結果:縮めたときに読めるかを、自分の目で確かめた

確かめたことを、そのまま書きます。

HTML の要素は33個で、閉じ忘れはありませんでした。読み込んだ時点で、1920×1080の待機画面と、その下に480×270の縮小見本が並んで出ます。

読めるかどうかは、縮小見本を自分の目で見て決めました。ここは数字では出ません。文字が何pxあれば読める、という線引きは、読む人の画面の大きさでも変わります。最後は目で見るしかありません。

暗くする量を変えますと、文字の読みやすさが変わりました。増やすほど文字は浮き、増やしすぎますと絵が見えなくなります。絵ごとに、この量を決め直す必要があります。

使えた点。同じ指示を入れ直すだけで、候補になる絵が何枚も出てきました。待機画面は一枚あれば済むものですので、たくさん出して一枚選ぶ、という進み方はわたくしに合っていました。出てきた絵を敷いて文字を重ねる処理も、コードで書けました。

そのままでは足りなかった点。一度出た絵を、あとで同じように出し直すことはできませんでした。気に入った絵は、その場で書き出して残しておくしかありません。あとから少しだけ構図を変えたいと思っても、指示を書き換えて出し直せば、別の絵になります。待機画面は長く使うものですから、この点は覚えておいたほうがよさそうです。

確かめていないことも書いておきます。わたくしが見たのは、自分の手元の画面と、OBS のブラウザソースに読み込んだ画面までです。実際の配信で何人に見られ、どう思われたかは分かりません。配信をしていないので、そこは書けません。

使うならここに気をつける:文字と余白の扱い

試して分かったつまずきどころと、その避け方を置きます。

一つ目、文字が絵に埋もれること。避け方は、影と縁取りと、背景を暗くする量の三つを重ねることです。一つだけでは心細いので、わたくしは三つとも入れておきました。暗くする量は、絵を変えたら決め直します。

二つ目、絵の中に文字を描かせてしまうこと。避け方は、言葉を絵として描かせないことです。文字はあとから重ねます。絵の中に文字を入れると、読めない形になりやすく、直しようがありません。重ねておけば、あとから書き換えられます。

三つ目、余白が足りないこと。避け方は、文字の位置、大きさの差、行間を先に決めておくことです。余白は、絵を出してから作ろうとすると足りません。先に確保しておきます。

四つ目、数字が動くこと。避け方は、数字だけ等幅の書体にすることです。1分ごとに更新する画面では、ここでつまずきます。

五つ目、原寸だけで決めてしまうこと。避け方は、縮めた見え方を同じ画面に並べて置くことです。原寸で読めても、縮んで読めなければ用を足しません。

六つ目、気に入った絵を残し忘れること。避け方は、選んだ時点で書き出すことです。

七つ目、権利の扱い。生成した絵を配信で使ってよいかどうかは、使う道具の規約によります。ここは、わたくしからはっきりと書けることではありません。使う前に、その道具の規約を読んでください。

まとめ:配信の待機画面づくりに、画像生成AIは使えるのか

向く場面から置きます。一枚あれば済む静止の絵が要るとき。急いでいるとき。絵の候補をたくさん見てから選びたいとき。文字をあとから重ねる前提で作るとき。この四つです。

向かない場面も置きます。自分の絵柄を決めたいとき。同じ一枚を何度も作り直したいとき。絵の中に文字やロゴを入れて、それで完成させたいとき。権利の扱いに確信が持てないとき。

まとめますと——ではなく、最後に一度だけ書きます。待機画面づくりに、画像生成AIは使えると思います。ただし、絵は下地として使う、文字はあとから重ねる、縮めて確かめる。この三つを守るなら、という条件つきです。

長く使う一枚をこれから決める、という場面でしたら、自分の手で描いた絵や、誰かに描いてもらった絵のほうが落ち着くと思います。急ぎの一枚が要るとき、あるいは絵柄の候補を広く見たいときだけ、そっと頼る。わたくしの手元では、そんな距離感になりました。

出どころ

この記事で触れた、わたくし以外のものの出どころです。

OBS Studio(作った待機画面をブラウザソースとして読み込んで確認した道具)
– 公式サイト:https://obsproject.com/ja/
– GitHub:https://github.com/obsproject/obs-studio
– 公式 X:@OBSProject

Noto Sans JP(コードの中で指定している書体の一つ)
– Google Fonts:https://fonts.google.com/noto/specimen/Noto+Sans+JP
– GitHub:https://github.com/notofonts/noto-cjk
– Google が公開している Noto ファミリーの日本語の書体です。SIL Open Font License で配布されています。

コードの中で名前に触れているそのほかの書体(Hiragino Sans、Yu Gothic、Meiryo、Courier New、Consolas)は、お使いの環境に備わっているものです。入っていないときは、次に書いてある書体が使われます。

この記事の HTML と JavaScript は、わたくしが書いたものです。

画像生成AI の名前は、本文に書いたとおり出していません。版によって出てくる絵が変わりますので、名前を出しても読む方の手元で同じ結果になるとは限らないためです。

コメント

タイトルとURLをコピーしました