LayoutSpec 사양서 — 나만의 "가사 배치" 만들기
이 문서는 일본어 원문의 번역본입니다. 일본어판이 정본이며, 내용에 차이가 있을 경우 일본어판이 우선합니다.
이 문서는 movlyric의 가사 배치(레이아웃)의 정의인 "LayoutSpec"의 완전한 사양입니다. 앱의 "스타일 에디터 → 「배치」 탭 → 배치 프리셋 → 「사용자 지정」 → AI로 만들기…"에서 사용합니다.
이름이 비슷한 별개의 것이 2개 있습니다. 혼동하지 마세요.
| 무엇을 정하는가 | 사양서 |
|---|---|
| 가사를 화면의 어디에 둘지(이 문서) | LAYOUT_SPEC(가사 배치) |
| 가사 글자가 어떻게 움직일지(등장·퇴장·명멸) | ANIMATION_SPEC(문자 움직임) |
| 가사의 뒤에 그리는 도형·선 | GRAPHIC_SPEC(배경 이펙트) |
이 문서만 읽은 AI(또는 사람)가 앱에 그대로 붙여 넣어 통과하는 JSON을 작성할 수 있게 하는 것이 목적입니다. 문서 끝에 AI에게 전달할 프롬프트 템플릿이 있습니다. 이 문서 전체를 프롬프트의 일부로 붙여 넣어도 작동합니다.
1. 개요
LayoutSpec은 "구절 하나 분량의 가사를 화면의 어디에, 어떻게 배열할지"만을 정합니다. 시간에 따라 변화하는 요소는 일절 없습니다(움직임은 AnimationSpec의 담당).
정해지는 것은 다음 3가지입니다.
- 위치 — 화면의 어느 점을 기준으로 둘지(
anchors/placement) - 짜임 — 가로쓰기인지 세로쓰기인지, 몇 줄까지 줄바꿈할지(
writing/maxLines/lineGap/scaleMode) - 흐트러뜨림 — 똑바로 배열하지 않고 흐트러뜨릴지(
scatter/baselineRotateDeg/path/charScaleRange/focusWord)
난수를 사용하는 항목은 모두 곡마다 고정됩니다. 같은 곡·같은 스타일이면 매번 같은 결과가 되고,
미리보기와 내보내기도 일치합니다(placement: "random" / scatter / charScaleRange /
focusWord.pick: "random"은 모두 스타일의 seed로 정해집니다).
2. JSON 형식의 완전한 사양
2.1 루트: LayoutSpec
{
"writing": "horizontal",
"anchors": [{ "x": 0.5, "y": 0.5, "align": "center" }],
"placement": "fixed"
}
| 키 | 필수 | 타입 | 의미 |
|---|---|---|---|
writing |
✅ | "horizontal" | "vertical" |
가로쓰기 / 세로쓰기(위→아래·열은 오른쪽→왼쪽) |
anchors |
✅ | Anchor의 배열(1개 이상) | 배치 후보 |
placement |
✅ | "fixed" | "cycle" | "random" |
구절을 후보에 어떻게 배정할지 |
maxLines |
정수 1 이상 | 줄바꿈 줄 수(세로쓰기는 열 수). 기본값 1 = 줄바꿈하지 않음 | |
lineGap |
숫자 0 이상 | 줄 간격(열 간격). 글자 크기에 대한 비율. 기본값 0.15 | |
scaleMode |
"fixed" | "fill" |
fill = 화면 너비(세로쓰기는 높이) 가득 채우는 큰 짜임. 기본값 fixed |
|
scatter |
Scatter | 글자마다의 흐트러뜨림 | |
baselineRotateDeg |
숫자 | 구절 전체의 기울기(도). 기본값 0 | |
path |
Path | 직선이 아닌 배열 방식(가로쓰기 전용) | |
charScaleRange |
[최소, 최대] |
글자마다 크기의 흔들림(가로쓰기 전용) | |
focusWord |
FocusWord | 단어 1개만 확대(가로쓰기 전용) |
기본값과 같은 값의 키는 쓰지 마세요. 예를 들어 maxLines: 1이나 scaleMode: "fixed"는
쓰지 않고 생략합니다(생략과 같은 의미이며, 쓰면 저장 내용이 쓸데없이 늘어납니다).
2.2 Anchor(배치 후보)
{ "x": 0.5, "y": 0.88, "align": "center" }
| 키 | 필수 | 타입 | 의미 |
|---|---|---|---|
x |
✅ | 0..1 | 화면의 왼쪽 끝 0 ~ 오른쪽 끝 1 |
y |
✅ | 0..1 | 화면의 위쪽 끝 0 ~ 아래쪽 끝 1 |
align |
✅ | "start" | "center" | "end" |
앵커를 기준으로 가사를 어느 쪽으로 뻗게 할지 |
align은 가로쓰기라면 왼쪽 정렬 / 가운데 정렬 / 오른쪽 정렬, 세로쓰기라면 위쪽 정렬 / 가운데 정렬 / 아래쪽 정렬입니다.
화면 끝에 둘 때는 align을 맞추세요. 예를 들어 x: 0.08에 align: "center"를
지정하면 긴 구절이 화면 왼쪽으로 삐져나갑니다. 왼쪽 끝에 붙이고 싶다면 align: "start"입니다.
2.3 placement(구절마다의 배정)
| 값 | 의미 |
|---|---|
"fixed" |
항상 1번째 후보를 사용(후보가 1개면 이것) |
"cycle" |
구절 순서대로 후보를 순환함 |
"random" |
후보 중에서 무작위로 선택(곡마다 고정) |
후보가 1개일 때는 3가지 모두 같은 결과가 됩니다. 후보를 2개 이상 썼을 때만 의미를 가집니다.
2.4 Scatter(글자마다의 흐트러뜨림)
{ "offsetRatio": 0.25, "rotateDeg": 8 }
| 키 | 필수 | 타입 | 의미 |
|---|---|---|---|
offsetRatio |
✅ | 숫자 0 이상 | 위치의 어긋남 폭. 글자 크기에 대한 비율 |
rotateDeg |
✅ | 숫자 0 이상 | 회전의 흔들림 폭(도) |
둘 다 곡마다 고정입니다. 0.3 / 12 정도면 상당히 흐트러진 인상이 됩니다.
2.5 Path(직선이 아닌 배열 방식·가로쓰기 전용)
{ "kind": "wave", "amplitude": 0.3, "period": 8 }
| 키 | 필수 | 타입 | 의미 |
|---|---|---|---|
kind |
✅ | "wave" | "arc" | "steps" |
파동 / 호 / 계단 |
amplitude |
✅ | 숫자 | 흔들림 폭. 글자 크기에 대한 비율 |
period |
숫자 0보다 큼 | 몇 글자로 한 바퀴 도는지. 생략 시 wave 8 / steps 3 |
wave와 arc는 글자의 회전이 곡선의 접선을 따라갑니다(배열을 따라 기울어짐).
arc는 period를 사용하지 않습니다(구절 전체로 하나의 호를 그림).
여러 줄일 때는 각 줄마다 독립적으로 적용됩니다.
2.6 charScaleRange(글자 크기의 흔들림·가로쓰기 전용)
[0.85, 1.3]
[최소, 최대]의 2요소. 글자마다 곡마다 고정된 배율이 정해집니다.
글자 크기와 보내기 폭 양쪽에 영향을 주므로, 줄 폭 계산·줄바꿈·가운데 정렬도 흔들림까지 포함해 일관됩니다.
2.7 FocusWord(단어 1개만 확대·가로쓰기 전용)
{ "scale": 1.4, "pick": "longest" }
| 키 | 필수 | 타입 | 의미 |
|---|---|---|---|
scale |
✅ | 숫자 0보다 큼 | 대상 단어의 배율 |
pick |
✅ | "longest" | "random" |
글자 수 최장(동수면 첫 번째) / 무작위(곡마다 고정) |
charScaleRange와 함께 쓰면 배율은 곱셈이 됩니다.
3. 흔한 실수(금지 사항)
- 세로쓰기에
path/charScaleRange/focusWord를 쓰지 않기. 엔진이 가로쓰기에서만 해석하므로, 써도 아무 일도 일어나지 않습니다(오류도 나지 않아 알아차릴 수 없습니다). anchors를 빈 배열로 하지 않기. 배치할 곳이 없어져 가사가 표시되지 않습니다.x/y에 픽셀 값을 쓰지 않기. 0~1의 비율입니다(960이 아니라0.5).- 기본값과 같은 키를 쓰지 않기(
maxLines: 1/lineGap: 0.15/scaleMode: "fixed"/placement가 사실상 무의미한 후보 1개에서의"cycle"등). - 시간이나 소리에 반응하는 값을 쓰지 않기. LayoutSpec에 수식·애니메이션은 없습니다. 움직이고 싶다면 AnimationSpec(ANIMATION_SPEC(문자 움직임))의 담당입니다.
- 존재하지 않는 속성·주석·끝 쉼표를 쓰지 않기.
4. 예시
4.1 화면 아래쪽에 한 줄(가라오케의 정석)
{
"writing": "horizontal",
"anchors": [{ "x": 0.5, "y": 0.88, "align": "center" }],
"placement": "fixed"
}
4.2 위아래로 나누어 2줄까지 줄바꿈
{
"writing": "horizontal",
"anchors": [
{ "x": 0.5, "y": 0.3, "align": "center" },
{ "x": 0.5, "y": 0.7, "align": "center" }
],
"placement": "cycle",
"maxLines": 2,
"lineGap": 0.4
}
구절마다 위→아래→위… 순으로 바뀝니다.
4.3 세로쓰기로 오른쪽부터 3열
{
"writing": "vertical",
"anchors": [{ "x": 0.82, "y": 0.15, "align": "start" }],
"placement": "fixed",
"maxLines": 3,
"lineGap": 0.5
}
세로쓰기는 위에서 아래로 흐르고, 열은 오른쪽에서 왼쪽으로 늘어납니다. align: "start"로 위쪽 끝을 맞추고 있습니다.
4.4 화면 가득한 큰 짜임을 비스듬히
{
"writing": "horizontal",
"anchors": [{ "x": 0.5, "y": 0.5, "align": "center" }],
"placement": "fixed",
"scaleMode": "fill",
"baselineRotateDeg": -6
}
scaleMode: "fill"은 구절을 화면 너비 가득까지 확대하므로, 글자 수에 따라 크기가 달라집니다.
4.5 네 귀퉁이에 흩어 흐트러뜨리기
{
"writing": "horizontal",
"anchors": [
{ "x": 0.2, "y": 0.25, "align": "start" },
{ "x": 0.8, "y": 0.4, "align": "end" },
{ "x": 0.25, "y": 0.62, "align": "start" },
{ "x": 0.75, "y": 0.8, "align": "end" }
],
"placement": "random",
"scatter": { "offsetRatio": 0.3, "rotateDeg": 12 },
"charScaleRange": [0.85, 1.3]
}
끝쪽 앵커에는 align을 start / end로 맞춰, 삐져나오지 않도록 하고 있습니다.
4.6 파도치게 해서 단어 1개를 강조
{
"writing": "horizontal",
"anchors": [{ "x": 0.5, "y": 0.5, "align": "center" }],
"placement": "fixed",
"path": { "kind": "wave", "amplitude": 0.35, "period": 6 },
"focusWord": { "scale": 1.5, "pick": "longest" }
}
5. AI용 프롬프트 템플릿
이 문서 전체를 붙여 넣은 다음, 이어서 아래를 붙여 넣으세요.
여기서부터 복사
당신은 movlyric의 가사 레이아웃 설계자입니다. 위에 붙여 넣은 "LayoutSpec 사양서"를 엄밀히 따라, LayoutSpec의 JSON을 하나 작성해 주세요.
출력 규칙:
- JSON만 출력한다(설명문·서두·맺음말은 쓰지 않는다. JSON을 코드 펜스로 감싸는 것은 가능)
- 루트에 쓸 수 있는 것은 writing, anchors, placement, maxLines, lineGap, scaleMode, scatter, baselineRotateDeg, path, charScaleRange, focusWord뿐이다
- writing, anchors, placement는 필수. 그 외에는 필요할 때만 쓴다
- **기본값과 같은 키는 쓰지 않는다**(maxLines:1 / lineGap:0.15 / scaleMode:"fixed")
- writing은 "horizontal" / "vertical" 중 하나
- anchors는 1~8개. 각 요소는 {"x":0..1, "y":0..1, "align":"start"|"center"|"end"}
- x, y는 **화면에 대한 비율**이다(픽셀이 아니다). 0.5가 중앙
- 화면 끝에 붙일 때는 align을 start / end로 맞춘다(center인 채로는 삐져나온다)
- placement는 "fixed" / "cycle" / "random". anchors가 1개면 "fixed"로 한다
- scatter는 {"offsetRatio":0 이상, "rotateDeg":0 이상}
- path는 {"kind":"wave"|"arc"|"steps", "amplitude":숫자, "period":0보다 큼(선택)}
- charScaleRange는 [최소, 최대]의 2요소
- focusWord는 {"scale":0보다 큼, "pick":"longest"|"random"}
- **writing이 "vertical"일 때는 path / charScaleRange / focusWord를 쓰지 않는다**(무시되기 때문)
- 시간·소리·난수에 반응하는 값이나 수식은 쓸 수 없다(움직임은 별도 사양인 AnimationSpec의 담당)
- 존재하지 않는 속성·주석·끝 쉼표를 쓰지 않는다
만들고 싶은 가사 배치:
{여기에 만들고 싶은 배치를 적는다}
(기존 레이아웃을 수정하고 싶을 때만, 아래에 현재 JSON을 붙여 넣으세요. 새로 만드는 경우에는 이 절 전체를 삭제하세요)
현재 JSON:
{현재 JSON}
여기까지 복사