GPT-6 Sol・Claude Opus 5.5 提供開始GPT-6 Sol の料金は 5.6 Sol の半額

Seedance 2.0 のリップシンク:セリフ用プロンプトと4本の実例

口の動きはプロンプトで決まり、自分の録音も出力の音声に残りました。

Kiki, OmniaKey COO読了目安 11分
このページの内容

Seedance 2.0 はプロンプトで口を動かします。音声のオンとオフだけが違う2本は、どちらも口が閉じた状態から大きく開くまで動きました。自分で録った1.8秒の音声を reference_to_video で送ると、出力の音声トラックにそのセリフが戻ってきます。音声だけのリクエストは受け付けられません。BytePlus の料金は720Pで秒$0.15、5秒なら$0.75です。

先に結論

  • 口の動きはプロンプトから来ます。同じプロンプトの5秒720Pを2本、音声ありと音声なしで作ると、どちらも口が閉じる → 少し開く → 大きく開くの順に動きました。
  • 音声だけでは通りません。2.0 系は参照画像か参照動画が最低1つ必要です(BytePlus のタスク作成 API、2026年11月3日に確認)。
  • 参照音声は最大3本、1本あたり2〜15秒、合計15秒、wav か mp3、1本15 MBまでです。
  • 音声はちゃんと使われます。1.8秒の mp3 が出力の音声トラックに戻り、セリフはそのままでした。
  • BytePlus の秒単価なら720Pで秒$0.15、5秒で$0.75です。
  • 同じ日に作った4本は、720P 音声あり、720P 音声なし、正方形720Pのイメージtoビデオ、480P の参照音声です。

Seedance 2.0 はリップシンクできるのか

公式ドキュメントに「リップシンク」という言葉は出てきません。書かれているのは、映像と音声をまとめて生成するという説明と、セリフをダブルクォートで囲むという一案だけです。

ByteDance の Seed ページは Seedance 2.0 を「a unified multimodal audio-video joint generation architecture that supports text, image, audio, and video inputs」と説明しています(2026年11月3日に確認)。BytePlus ModelArk のガイドも同じで、できることの一覧に「Generate videos with audio」とあります(ページの更新は2026年9月29日)。

この範囲で言葉を探すと、「lip sync」も「talking」も一度も出てきません。「mouth」はサンプルのプロンプトに1回、「dialogue」は2回で、どちらも音声生成の話です。いちばん具体的なのは「It is recommended to put dialogue content in double quotes to optimize the audio generation effect」という一文です(タスク作成 API、更新は2026年10月28日)。

つまり、映像も音声も出るけれど、音素ごとに唇が合うとはどのページにも書かれていない、という状態です。今回の4本は、音声ありでもなしでも口が動きました。プロンプトに「しゃべっている」と書けば口が動く、という映像モデルとして素直な結果です。

試している人はいます。2026年2月18日の Hacker News のコメントは、曲と動画を一緒に送ったところ「the model did reference the singing music I provided, but it reinterpreted it on its own」だったと書いています(コメント)。2026年6月3日の GitHub issue は「Mouth movements match phoneme timing」を合格条件にしていますが、この測定は公式ドキュメントにはありません(issue)。

セリフをプロンプトに書く

まず人物、次にセリフをダブルクォートで、最後に口と顔の動きです。今回のプロンプトは人物と口の動きを書いて、ダブルクォートを忘れました。だからセリフはモデルが自分で決めています。

今回使った書き方は5つです。

  1. 人物を一文で決める。クレイのストップモーション、丸い大きな眼鏡、ニットのマフラー、台所のテーブル。
  2. 何をするかを書く。カメラに向かって短いセリフをひとつ。
  3. セリフをそのままダブルクォートで囲む。BytePlus の推奨です。
  4. 口と顔を書く。言葉に合わせて口が開閉し、眉が動き、強調するところで首が少し傾く。
  5. カメラと照明を固定する。動くのは演技だけになります。

4本とも同じプロンプトです。そのまま載せます。

A stop-motion clay character with big round glasses and a knitted scarf sits at a kitchen table, looks straight into the camera, and speaks one short line to the viewer. The mouth opens and closes in time with the words, the eyebrows move with the sentence, and the head tilts slightly on the stressed word. Fixed camera, warm lamp light, a mug of tea in front of them, gentle 24 fps stop-motion feel.

足りないのはセリフそのものです。「短いセリフをひとつ」としか書いていないので、言葉はモデルが選びました。音声をオンにすると「You look like you could use a warm hug.」と言っていて、この一文はリクエストのどこにもありません。セリフを決めたいなら、3番目を送信前に足してください。

口の動きは0.5秒ごとに1コマ抜き出して見ました。音声ありは閉じた状態から始まり、2〜7コマ目が少し開き、8コマ目で大きく開き、また少し開いて閉じます。音声なしは、閉じる、少し開く、大きく開く、少し開く、大きく開く、最後の2コマが少し開いた状態でした。

丸い眼鏡とマフラーのクレイ人形の顔を0.5秒ごとに切り出した10コマ。最初と最後のコマは口を閉じ、その間は開いていて、8コマ目がいちばん大きく開いています。
音声あり、0.5秒ごとに1コマ:1コマ目は口を閉じ、2〜7コマ目は少し開き、8コマ目で大きく開き、10コマ目でまた閉じています。Seedance 2.0 を OmniaKey 経由で、テキストから動画、5秒、720P、2026年11月3日に生成。
濃い緑のマフラーを巻いたオレンジ色のクレイ人形の顔を0.5秒ごとに切り出した10コマ。音声トラックのない動画で、口が開いたり閉じたりしています。
音声を切って同じプロンプトで作った動画でも、口は開いたり閉じたりし、5・6・8コマ目で大きく開いています。ファイルに音声トラックはありません。5秒、720P、2026年11月3日に生成。

同じ文でも別の人物になります。音声なしのほうは体がオレンジ、髪は灰褐色の分け目、マフラーは濃い緑でした。人物の一文は提案として書くのがちょうどいいです。2本ともカメラは動いていません。「Fixed camera」の効果です。

自分の音声は送れるのか

送れます。同じリクエストに参照画像か参照動画を1つ入れてください。1.8秒の mp3 を参照音声として送ると、セリフが出力の音声トラックに戻りました。

使ったのは reference_to_video で、中身は3つです。クレイ人形の静止画を参照画像、自分の録音を参照音声、そして上のプロンプトです。設定は480P、5秒、音声生成オン。OmniaKey は202を返し、タスク task_3Ay0Ktf2mbYXh9GNZpZ5j9kGyOIzz8Pv は成功、120.2秒で MP4 が届きました。864×496、音声は5.06秒の aac です。

音声を書き起こすと「Why tow it? I told you the keys were in the drawer.」でした。後半は自分で録ったセリフと一言も違いません。最初の3語は送ったファイルにありません。書き起こしでは、自分の息なのかモデルが足した言葉なのか区別できないので、聞こえたままを載せています。

帽子とオレンジのマフラーの茶色いクレイ人形が木のテーブルの前に座る10コマ。口が閉じたり開いたりし、カップの湯気が動いている。
自分の録音を参照音声に:口が動き、カップの湯気も揺れ、出力の音声に送ったセリフが入っています。5秒、480P、2026年11月3日に生成。

10コマ、0.5秒ごとでは、口が動いていることしか分かりません。音素ごとの一致を見るには、もっと細かいコマと専用の測定が必要です。今回はそこまでやっていないので、唇がすべての音に合ったとは書けません。

制限は BytePlus のタスク作成 API によります(2026年11月3日に確認)。音声は最大3本、1本2〜15秒、合計15秒、wav か mp3、1本15 MBまで。音声は公開 URL、Base64、アセット ID のどれでも送れます。音声だけは通りません。原文は「Audio-only input is not supported; include at least one reference image or video.」です。

実在の人物は入れないでください。2.0 系の参照画像と参照動画に本物の顔があると弾かれます。しゃべる映像はクレイ、イラスト、CG の人物で作ってください。

画像から動画にするとカメラが勝手に動く

イメージtoビデオでは、立っている全身のショットから、4コマ目で座った台所の寄りに切り替わりました。プロンプトにはカットもカメラ移動も書いていません。

最初のコマは今回のために作った静止画です。クレイ人形が白い壁の前に立ち、両手を下げて全身が入り、1254×1254ピクセル。プロンプトは上のもので、台所のテーブルに座る、と書いてあります。1〜3コマ目は立ち姿、4コマ目でテーブル前の寄りに飛び、そのまま続きます。口は7コマ目まで閉じ、8コマ目で少し開き、9〜10コマ目で大きく開きます。音声は「Well, what do you think?」と布ずれの音でした。

正方形の動画の10コマ。最初は立っているクレイ人形で、途中から台所のテーブルに座った姿になり、口が開く。
イメージtoビデオ:立った最初のコマは少しだけ続き、プロンプトが求めた台所の座り姿に切り替わります。画像にはない構図です。5秒、720P、2026年11月3日に生成。

ここで見るのは構図で、口ではありません。このモデルでは、人物がどこにいてカメラがどう動くかはプロンプトで決まり、最初のコマは人物の見た目を決めます。大事なショットはプロンプトに書いてください。

届いた動画は1280×720ではなく960×960でした。正方形の画像を最初のコマにして比率を送らなかったため、API の初期値が画像に合わせたからです。比率を決めたいときは ratio を自分で書いてください。解像度の名前から推測しないほうが安全です。

Python で送る

タスクを作る、ID を聞く、MP4 を落とす。3つのリクエストで、標準ライブラリだけで動きます。同じ補助関数が3つの操作を兼ねます。

python
import base64
import json
import os
import time
import urllib.error
import urllib.request
import uuid

API = "https://api.omniakey.com/v1/media/tasks"
KEY = os.environ["OMNIAKEY_API_KEY"]
MODEL = "dreamina-seedance-2-0-260128"

PROMPT = (
    "A stop-motion clay character with big round glasses and a knitted scarf sits at a kitchen table, "
    "looks straight into the camera, and speaks one short line to the viewer. The mouth opens and closes "
    "in time with the words, the eyebrows move with the sentence, and the head tilts slightly on the "
    "stressed word. Fixed camera, warm lamp light, a mug of tea in front of them, gentle 24 fps "
    "stop-motion feel."
)

MIME = {".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg",
        ".webp": "image/webp", ".mp3": "audio/mpeg", ".wav": "audio/wav"}


def call(method, url, body=None, idempotency_key=None):
    headers = {"Authorization": f"Bearer {KEY}", "User-Agent": "omniakey-example/1"}
    data = None
    if body is not None:
        headers["Content-Type"] = "application/json"
        data = json.dumps(body).encode()
    if idempotency_key:
        headers["Idempotency-Key"] = idempotency_key
    req = urllib.request.Request(url, data=data, headers=headers, method=method)
    with urllib.request.urlopen(req, timeout=120) as resp:
        return json.load(resp)


def data_url(path, limit_mb):
    raw = open(path, "rb").read()
    if len(raw) > limit_mb * 1024 * 1024:  # 画像は30 MB、音声は15 MB
        raise ValueError(f"{path} is over {limit_mb} MB, the limit for this input type")
    mime = MIME[os.path.splitext(path)[1].lower()]
    return f"data:{mime};base64," + base64.b64encode(raw).decode()


def wait(task_id, timeout=900, step=5):
    deadline = time.time() + timeout
    while time.time() < deadline:
        state = call("GET", f"{API}/{task_id}")
        if state["status"] not in ("queued", "processing"):
            return state
        time.sleep(step)
    raise TimeoutError(f"{task_id} is still running: poll it again, do not resubmit")


def run(body, out_path):
    task_id = call("POST", API, body, str(uuid.uuid4()))["id"]
    print("task", task_id, flush=True)
    state = wait(task_id)
    if state["status"] != "succeeded":
        raise RuntimeError(state.get("error") or state["status"])
    signed = state["assets"][0]["url"]  # 期限があるので、届いたらすぐ落とす
    req = urllib.request.Request(signed, headers={"User-Agent": "omniakey-example/1"})
    with urllib.request.urlopen(req, timeout=300) as resp, open(out_path, "wb") as f:
        f.write(resp.read())
    return task_id


def reference_to_video(prompt, image_path, audio_path, out_path):
    body = {
        "model": MODEL,
        "type": "video",
        "operation": "reference_to_video",
        "input": {
            "prompt": prompt,
            "media": [
                {"type": "reference_image", "url": data_url(image_path, 30)},
                {"type": "reference_audio", "url": data_url(audio_path, 15)},
            ],
        },
        "parameters": {"duration": 5, "resolution": "480P", "ratio": "16:9",
                       "generate_audio": True, "watermark": False},
    }
    return run(body, out_path)


if __name__ == "__main__":
    print("done", reference_to_video(PROMPT, "character.png", "line.mp3", "line-clip.mp4"))

残しておきたいのは3つです。リクエストごとに新しい Idempotency-Key を付けると、再送しても元のタスクが返り、二重に課金されません。待ちループは15分で諦めて例外を出します。時間切れはまだ描画中という意味なので、送り直すともう1本買うことになります。ダウンロードは最初に成功した時点で行います。assets[0].url は期限付きの署名付きリンクだからです。

ほかの2つの操作は本文だけ差し替えます。補助関数はそのままです。

python
def text_to_video(prompt, out_path):
    body = {
        "model": MODEL,
        "type": "video",
        "operation": "text_to_video",
        "input": {"prompt": prompt},
        "parameters": {"duration": 5, "resolution": "720P", "ratio": "16:9",
                       "generate_audio": True, "watermark": False},
    }
    return run(body, out_path)


def image_to_video(prompt, image_path, out_path):
    body = {
        "model": MODEL,
        "type": "video",
        "operation": "image_to_video",
        "input": {"prompt": prompt,
                  "media": [{"type": "first_frame", "url": data_url(image_path, 30)}]},
        "parameters": {"duration": 5, "resolution": "720P", "ratio": "adaptive",
                       "generate_audio": True, "watermark": False},
    }
    return run(body, out_path)

項目はこれだけです。model、type、operation、input.prompt、input.media[](type は first_frame、reference_image、reference_video、reference_audio)、そして parameters.duration、resolution、ratio、generate_audio、watermark。同じ形は OmniaKey の 動画 API のドキュメント にあり、作成・確認・ダウンロードの流れは メディア API の概要 にまとめてあります。このモデルは Seedance 2.0 のモデルページ から試せます。

料金

BytePlus は秒単位の課金です。5秒、16:9、動画入力なしの場合の定価は次のとおりです(ModelArk の料金ページ、2026年11月3日に確認)。

出力秒あたり5秒
480p$0.07$0.35
720p$0.15$0.76
1080p$0.37$1.87
4K$0.78$3.89

秒単価で計算すると、ページの表とは1セントだけずれます。5秒 × $0.15 = $0.75で、720p の行は$0.76です。今回のテキストからの2本は720Pの5秒なので、どちらも$0.75の位置になります。

動画を入力するリクエストは別の計算で、もっと高くなります。参照動画のタスクは上の表には当てはまりません(ModelArk の料金ページ、2026年11月3日に確認)。OmniaKey は生成した秒数で課金し、失敗やキャンセルで終わったタスクは返金します(メディア API の概要、2026年11月3日に確認)。現在の価格は モデルページ にあります。

つまずきやすいところ

  • 音声だけでは弾かれます。参照画像か参照動画を同じリクエストに入れてください。
  • 実在の顔は通りません。参照画像や参照動画に本物の人がいると止められます。クレイやイラスト、CG の人物にしてください。
  • generate_audio は初期値がオンです。無音にしたいときは明示的に切り、セリフを出したいときは初期値のまま、プロンプトに「しゃべっている」と書きます。
  • 出力サイズは比率で決まります。720P のテキストからは1280×720、比率なしの正方形の静止画からは960×960、480P の16:9は864×496でした。
  • 速さは安定しません。同じ5秒の4本が259.0秒、202.0秒、155.3秒、120.2秒でした。2〜5分を見て、ポーリングで待ってください。
  • 長さは出力が4〜15秒、参照クリップは1本2〜15秒で合計15秒まで。4本とも24 fps でした。

よくある質問

WAV を送れば、その音に合わせて口が動きますか

参照画像と一緒に送れば、モデルは音声を使います。自分たちの録音は出力の音声に戻りました。唇がファイルの一音一音に合っているかは、今回のコマからは判断できません。0.5秒に1コマでは口が動いていることまでしか分からず、一致を測ったものではありません。

音声だけのリクエストが拒否されたのはなぜですか

2.0 系は音声だけを入力として受け付けません。同じリクエストに参照画像か参照動画を足せば、音声も一緒に送れます。

セリフを出すとき、音声生成はオンとオフのどちらですか

オンのままにしてください。初期値がオンです。オフにすると音声トラックのないファイルになり、プロンプトにしゃべると書いてあれば口は動きます。音声なしの動画がその例です。

参照音声はどれくらいの長さまで使えますか

1本2〜15秒、最大3本、合計15秒、wav か mp3 で1本15 MBまでです(BytePlus のタスク作成 API、2026年11月3日に確認)。今回の1.8秒はこの下限より短いものの、Base64 のデータ URL として通りました。

出典

更新履歴

  • 2026年11月3日:初版。4本の動画はすべてこの日に生成しました。

この記事で紹介しているモデル

Seedance 2.0リップシンクAI 動画参照音声動画 APIPython

関連記事

最近の記事

人気のガイド