Seedance 2.0 립싱크: 대사 프롬프트와 실제 영상 4편
입 모양은 프롬프트를 따라갔고, 제 녹음은 출력 오디오로 돌아왔습니다.

이 페이지 목차
Seedance 2.0은 프롬프트를 따라 입을 움직입니다. 오디오만 켜고 끈 두 편 모두 입이 닫힌 상태에서 크게 벌어지는 데까지 움직였습니다. 제가 녹음한 1.8초 오디오를 reference_to_video로 보내면 출력 오디오 트랙에 그 대사가 돌아옵니다. 오디오만 보내면 거부됩니다. BytePlus 요금은 720P에서 초당 $0.15이고, 5초면 $0.75입니다.
먼저 결론
- 입 모양은 프롬프트에서 나옵니다. 같은 프롬프트로 5초 720P를 두 편, 오디오를 켠 것과 끈 것으로 만들었더니 둘 다 입이 닫힘 → 살짝 벌어짐 → 크게 벌어짐 순서로 움직였습니다.
- 오디오만으로는 통과하지 않습니다. 2.0 계열은 참조 이미지나 참조 영상이 최소 하나 필요합니다(BytePlus 작업 생성 API, 2026년 11월 3일 확인).
- 참조 오디오는 최대 3개, 개당 2~15초, 합계 15초, wav 또는 mp3, 개당 15 MB입니다.
- 오디오는 실제로 쓰입니다. 1.8초 mp3가 출력 오디오 트랙에 돌아왔고 대사는 그대로였습니다.
- BytePlus 초당 가격이면 720P에서 초당 $0.15, 5초에 $0.75입니다.
- 같은 날 만든 4편은 720P 오디오 있음, 720P 오디오 없음, 정사각형 720P 이미지→영상, 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"는 예시 프롬프트에 한 번, "dialogue"는 두 번 나오는데 둘 다 오디오 생성 이야기입니다. 가장 구체적인 문장은 "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 이슈는 "Mouth movements match phoneme timing"을 통과 조건으로 두지만, 그 측정은 공식 문서에 없습니다(이슈).
대사를 프롬프트에 쓰는 방법
먼저 인물, 다음에 대사를 큰따옴표로, 마지막에 입과 얼굴 움직임입니다. 이번 프롬프트는 인물과 입 모양을 썼지만 큰따옴표를 빠뜨렸습니다. 그래서 대사는 모델이 골랐습니다.
이번에 쓴 방식은 다섯 단계입니다.
- 인물을 한 문장으로 정합니다. 클레이 스톱모션, 큰 둥근 안경, 니트 목도리, 부엌 탁자.
- 무엇을 하는지 씁니다. 카메라를 보고 짧은 대사를 하나 말합니다.
- 대사를 그대로 큰따옴표로 감쌉니다. BytePlus 권고입니다.
- 입과 얼굴을 씁니다. 말에 맞춰 입이 열리고 닫히고, 눈썹이 문장을 따라가고, 강세에서 고개가 살짝 기웁니다.
- 카메라와 조명을 고정합니다. 화면에서 움직이는 것은 연기뿐입니다.
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초마다 한 프레임씩 뽑아 봤습니다. 오디오가 있는 편은 입을 닫은 채로 시작해 2~7번째 프레임에서 살짝 벌어지고, 8번째에 크게 벌어졌다가 다시 살짝 벌어지고 닫힙니다. 오디오가 없는 편은 닫힘, 살짝 벌어짐, 크게 벌어짐, 살짝 벌어짐, 크게 벌어짐, 마지막 두 프레임은 살짝 벌어진 상태였습니다.


같은 문장이 다른 인물로 나오기도 합니다. 오디오를 끈 편은 몸이 주황색, 머리는 회갈색 가르마, 목도리는 짙은 초록이었습니다. 인물을 정하는 문장은 제안으로 쓰는 편이 낫습니다. 두 편 모두 카메라는 움직이지 않았습니다. "Fixed camera" 덕분입니다.
내 오디오를 보낼 수 있나요
보낼 수 있습니다. 같은 요청에 참조 이미지나 참조 영상을 하나 넣으면 됩니다. 1.8초 mp3를 참조 오디오로 보냈더니 대사가 출력 오디오 트랙으로 돌아왔습니다.
쓴 것은 reference_to_video이고 요청에는 세 가지가 들어 있습니다. 클레이 인형 정지 이미지를 참조 이미지로, 제 녹음을 참조 오디오로, 그리고 위 프롬프트입니다. 설정은 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."였습니다. 뒷부분은 제가 녹음한 대사와 한 단어도 다르지 않습니다. 앞의 세 단어는 제가 보낸 파일에 없습니다. 받아쓰기로는 제 숨소리인지 모델이 덧붙인 말인지 구분되지 않아서, 들린 그대로 적었습니다.

10프레임, 0.5초 간격으로는 입이 움직인다는 것까지만 보입니다. 음소 단위 일치를 보려면 더 촘촘한 프레임과 전용 측정이 필요합니다. 이번에는 하지 않았으므로 입술이 모든 소리에 맞았다고 쓸 수 없습니다.
제한은 BytePlus 작업 생성 API를 따릅니다(2026년 11월 3일 확인). 오디오는 최대 3개, 개당 2~15초, 합계 15초, wav 또는 mp3, 개당 15 MB입니다. 오디오는 공개 URL, Base64, 에셋 ID 어느 것으로도 보낼 수 있습니다. 오디오만 보내는 것은 안 됩니다. 원문은 "Audio-only input is not supported; include at least one reference image or video."입니다.
이미지에서 영상으로 만들면 카메라가 스스로 움직입니다
이미지→영상은 서 있는 전신 샷에서 4번째 프레임에 앉은 부엌 근경으로 바뀌었습니다. 프롬프트에는 컷도 카메라 이동도 쓰지 않았습니다.
첫 프레임은 이번에 따로 만든 정지 이미지입니다. 클레이 인형이 밝은 흰 벽 앞에 서 있고 두 팔은 아래로, 전신이 들어가며 1254×1254픽셀입니다. 프롬프트는 위에 있는 것이고 부엌 탁자에 앉는다고 적혀 있습니다. 13번째 프레임은 서 있는 모습, 4번째에 탁자 앞 근경으로 뛰고 그대로 이어집니다. 입은 7번째 프레임까지 다물고, 8번째에 살짝 벌어지고, 910번째에 크게 벌어집니다. 오디오는 "Well, what do you think?"와 천 스치는 소리였습니다.

여기서 볼 것은 구도이고 입이 아닙니다. 이 모델에서는 인물이 어디에 있고 카메라가 어떻게 움직이는지를 프롬프트가 정하고, 첫 프레임은 인물의 생김새를 정합니다. 중요한 샷은 프롬프트에 쓰세요.
도착한 영상은 1280×720이 아니라 960×960이었습니다. 정사각형 이미지를 첫 프레임으로 쓰고 비율을 보내지 않아 API 기본값이 이미지를 따라간 결과입니다. 비율을 정하려면 ratio를 직접 쓰세요. 해상도 이름으로 짐작하지 않는 편이 안전합니다.
Python 요청
작업을 만들고, ID를 물어보고, MP4를 내려받습니다. 요청 세 번, 표준 라이브러리만 씁니다. 같은 보조 함수가 세 동작을 맡습니다.
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"))
남겨둘 만한 것은 세 가지입니다. 요청마다 새 Idempotency-Key를 붙이면 다시 보내도 원래 작업이 돌아오고 두 번 결제되지 않습니다. 대기 루프는 15분에서 예외를 내고 멈춥니다. 시간 초과는 아직 렌더링 중이라는 뜻이라 다시 보내면 한 편을 더 사는 셈입니다. 내려받기는 처음 성공한 순간에 합니다. assets[0].url은 만료되는 서명 링크이기 때문입니다.
나머지 두 동작은 본문만 바꿉니다. 보조 함수는 그대로입니다.
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입니다. 이번 텍스트→영상 두 편은 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초 네 편이 259.0초, 202.0초, 155.3초, 120.2초였습니다. 2~5분을 잡고 폴링으로 기다리세요.
- 길이는 출력 4
15초, 참조 클립은 개당 215초에 합계 15초까지입니다. 네 파일 모두 24 fps였습니다.
자주 묻는 질문
WAV를 보내면 그 소리에 맞춰 입이 움직이나요
참조 이미지와 함께 보내면 모델이 그 오디오를 씁니다. 저희 녹음은 출력 오디오로 돌아왔습니다. 입술이 파일의 소리 하나하나에 맞는지는 이번 프레임으로는 알 수 없습니다. 0.5초에 한 프레임으로는 입이 움직인다는 것까지만 보이고, 일치를 측정한 값은 아닙니다.
오디오만 보낸 요청은 왜 거부됐나요
2.0 계열은 오디오만 입력으로 받지 않습니다. 같은 요청에 참조 이미지나 참조 영상을 더하면 오디오도 함께 전달됩니다.
대사를 낼 때 오디오 생성은 켜야 하나요, 꺼야 하나요
켜두세요. 기본값입니다. 끄면 오디오 트랙이 없는 파일이 되고, 프롬프트에 말한다고 적혀 있으면 입은 움직입니다. 오디오를 끈 영상이 그 예입니다.
참조 오디오는 얼마나 길게 쓸 수 있나요
개당 2~15초, 최대 3개, 합계 15초, wav 또는 mp3에 개당 15 MB입니다(BytePlus 작업 생성 API, 2026년 11월 3일 확인). 이번에 보낸 1.8초는 이 하한보다 짧았지만 Base64 데이터 URL로 그대로 통과했습니다.
출처
- ByteDance Seed: Seedance 2.0, 2026년 11월 3일 확인
- BytePlus ModelArk: Seedance 2.0 가이드, 2026년 11월 3일 확인
- BytePlus ModelArk: 영상 생성 작업 API, 2026년 11월 3일 확인
- BytePlus ModelArk: 모델 요금, 2026년 11월 3일 확인
업데이트 기록
- 2026년 11월 3일: 첫 버전. 네 편의 영상은 모두 이날 만들었습니다.
이 글에서 다루는 모델
관련 글
Seedance 2.0 API 사용법: Python으로 텍스트 동영상과 이미지 동영상 만들기
스크립트 하나가 작업을 만들고, 상태를 확인하고, MP4를 저장합니다. 필드와 제한, 그리고 2026년 10월 30일에 만든 5초 영상 2개를 담았습니다.
Seedance 2.5 프롬프트 가이드: 구조, 예시, 실제 생성 영상
프롬프트 여섯 개와 실제 영상 여섯 편. Seedance 2.5가 지킨 것, 바꾼 것, 고치는 방법을 담았습니다.
Seedance 2.5 리뷰
의미 있는 업그레이드는 주변에 떠돌고 있는 검증되지 않은 4K 헤드라인이 아니라 30 2차 스토리텔링과 생산 관리입니다.
