mulmocast 2.18.0 → 2.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/schemas/mulmo_script.json +100 -0
- package/lib/actions/audio.js +4 -1
- package/lib/actions/avatar.js +4 -1
- package/lib/agents/add_bgm_agent.d.ts +13 -1
- package/lib/agents/add_bgm_agent.js +77 -4
- package/lib/cli/bin.js +0 -0
- package/lib/mcp/server.js +0 -0
- package/lib/methods/mulmo_studio_context.d.ts +2 -0
- package/lib/methods/mulmo_studio_context.js +19 -0
- package/lib/types/schema.d.ts +64 -0
- package/lib/types/schema.js +42 -0
- package/lib/types/type.d.ts +2 -1
- package/lib/utils/context.d.ts +32 -0
- package/lib/utils/jinglescript.d.ts +12 -0
- package/lib/utils/jinglescript.js +63 -0
- package/package.json +5 -4
- package/scripts/test/test_jinglescript_sound_effects.json +101 -0
- package/scripts/test/test_sound_effects.json +64 -0
- package/scripts/test/test_sound_effects_animation.json +441 -0
- package/scripts/test/test_captions_2.json~ +0 -278
- package/scripts/test/test_en_portrait.json~ +0 -31
- package/scripts/test/test_slide_image_ref_gemini_en.json~ +0 -289
- package/scripts/test/test_vertexai.json~ +0 -21
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mulmocast",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.19.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.node.js",
|
|
@@ -120,6 +120,7 @@
|
|
|
120
120
|
"clipboardy": "^5.3.2",
|
|
121
121
|
"dotenv": "^17.4.2",
|
|
122
122
|
"graphai": "^2.0.18",
|
|
123
|
+
"jinglescript": "^0.6.0",
|
|
123
124
|
"js-tiktoken": "^1.0.21",
|
|
124
125
|
"jsdom": "^29.1.1",
|
|
125
126
|
"marked": "^18.1.0",
|
|
@@ -149,7 +150,7 @@
|
|
|
149
150
|
"@types/jsdom": "^28.0.3",
|
|
150
151
|
"@types/node": "^26.6.4",
|
|
151
152
|
"@types/yargs": "^17.0.35",
|
|
152
|
-
"avatarscript": "^0.
|
|
153
|
+
"avatarscript": "^0.4.0",
|
|
153
154
|
"cross-env": "^10.1.0",
|
|
154
155
|
"esbuild": "^0.28.2",
|
|
155
156
|
"eslint": "^10.12.0",
|
|
@@ -179,7 +180,7 @@
|
|
|
179
180
|
"@remotion/shapes": "^4.0.533",
|
|
180
181
|
"@remotion/three": "^4.0.533",
|
|
181
182
|
"@remotion/transitions": "^4.0.533",
|
|
182
|
-
"avatarscript": "^0.
|
|
183
|
+
"avatarscript": "^0.4.0",
|
|
183
184
|
"onnxruntime-node": "^1.30.0",
|
|
184
185
|
"react": "^19.2.0",
|
|
185
186
|
"react-dom": "^19.2.0",
|
|
@@ -240,6 +241,6 @@
|
|
|
240
241
|
}
|
|
241
242
|
},
|
|
242
243
|
"engines": {
|
|
243
|
-
"node": ">=22.
|
|
244
|
+
"node": ">=22.13.0"
|
|
244
245
|
}
|
|
245
246
|
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$mulmocast": { "version": "1.1" },
|
|
3
|
+
"lang": "ja",
|
|
4
|
+
"title": "JingleScript sound effects test",
|
|
5
|
+
"audioParams": { "bgmVolume": 0.05 },
|
|
6
|
+
"beats": [
|
|
7
|
+
{
|
|
8
|
+
"text": "チク、タク、チク、タク。時計がなりますよ。",
|
|
9
|
+
"image": { "type": "textSlide", "slide": { "title": "チクタク、チーン" } },
|
|
10
|
+
"soundEffects": [
|
|
11
|
+
{
|
|
12
|
+
"source": {
|
|
13
|
+
"kind": "jinglescript",
|
|
14
|
+
"score": {
|
|
15
|
+
"format": "jinglescript/1",
|
|
16
|
+
"title": "Clock ticks into a chime",
|
|
17
|
+
"tempo": 120,
|
|
18
|
+
"length": { "seconds": 5 },
|
|
19
|
+
"cues": { "hit": { "seconds": 3 } },
|
|
20
|
+
"tracks": [
|
|
21
|
+
{
|
|
22
|
+
"instrument": "clock",
|
|
23
|
+
"reverb": false,
|
|
24
|
+
"gain": -4,
|
|
25
|
+
"notes": [{ "at": 0, "variant": ["tick", "tock"], "vel": 0.7, "repeat": { "every": 1, "until": "hit-1" } }]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"instrument": "glockenspiel",
|
|
29
|
+
"notes": [
|
|
30
|
+
{ "at": "hit", "pitch": ["C6", "G6"], "vel": 0.8 },
|
|
31
|
+
{ "at": "hit+1", "pitch": "C7", "vel": 0.4 }
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"volume": 0.7
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"text": "ポン、ポン、ポン! 星が三つ出てきました。",
|
|
43
|
+
"image": { "type": "textSlide", "slide": { "title": "ポン、ポン、ポン" } },
|
|
44
|
+
"soundEffects": [
|
|
45
|
+
{
|
|
46
|
+
"source": {
|
|
47
|
+
"kind": "jinglescript",
|
|
48
|
+
"score": {
|
|
49
|
+
"format": "jinglescript/1",
|
|
50
|
+
"tempo": 120,
|
|
51
|
+
"length": { "seconds": 2 },
|
|
52
|
+
"tracks": [
|
|
53
|
+
{
|
|
54
|
+
"instrument": "pop",
|
|
55
|
+
"reverb": false,
|
|
56
|
+
"notes": [
|
|
57
|
+
{ "at": 0, "pitch": "E6", "vel": 0.8 },
|
|
58
|
+
{ "at": 0.5, "pitch": "G6", "vel": 0.8 },
|
|
59
|
+
{ "at": 1, "pitch": "C7", "vel": 0.9 }
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"startAt": 0.2
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"text": "よくできました!",
|
|
71
|
+
"image": { "type": "textSlide", "slide": { "title": "よくできました" } },
|
|
72
|
+
"soundEffects": [
|
|
73
|
+
{
|
|
74
|
+
"source": {
|
|
75
|
+
"kind": "jinglescript",
|
|
76
|
+
"score": {
|
|
77
|
+
"format": "jinglescript/1",
|
|
78
|
+
"title": "Well done",
|
|
79
|
+
"tempo": 130,
|
|
80
|
+
"length": { "seconds": 3 },
|
|
81
|
+
"cues": { "hit": { "seconds": 0.7 } },
|
|
82
|
+
"tracks": [
|
|
83
|
+
{
|
|
84
|
+
"instrument": "marimba",
|
|
85
|
+
"notes": [
|
|
86
|
+
{ "at": 0, "pitch": "G4", "vel": 0.8 },
|
|
87
|
+
{ "at": 0.5, "pitch": "C5", "vel": 0.85 },
|
|
88
|
+
{ "at": "hit", "pitch": ["C5", "E5", "G5", "C6"], "vel": 1.0 }
|
|
89
|
+
]
|
|
90
|
+
},
|
|
91
|
+
{ "instrument": "marimba", "notes": [{ "at": "hit", "pitch": ["C3", "G3"], "vel": 0.9 }] }
|
|
92
|
+
]
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
"startAt": 0.8,
|
|
96
|
+
"volume": 0.8
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$mulmocast": { "version": "1.1" },
|
|
3
|
+
"lang": "ja",
|
|
4
|
+
"title": "Sound effects test",
|
|
5
|
+
"audioParams": { "bgmVolume": 0.1 },
|
|
6
|
+
"beats": [
|
|
7
|
+
{
|
|
8
|
+
"text": "時計の針が動いています。チク、タク、チク、タク。",
|
|
9
|
+
"image": { "type": "textSlide", "slide": { "title": "チクタク" } },
|
|
10
|
+
"soundEffects": [
|
|
11
|
+
{
|
|
12
|
+
"source": {
|
|
13
|
+
"kind": "url",
|
|
14
|
+
"url": "https://github.com/receptron/mulmocast-media/raw/refs/heads/main/soundeffects/opengameart/bart-ticking-clock/ticking_clock.wav"
|
|
15
|
+
},
|
|
16
|
+
"startAt": 0,
|
|
17
|
+
"volume": 0.6,
|
|
18
|
+
"loop": true
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"text": "ボールがはねました。そして、コインをゲット!",
|
|
24
|
+
"image": { "type": "textSlide", "slide": { "title": "ビヨーン、チャリン" } },
|
|
25
|
+
"soundEffects": [
|
|
26
|
+
{
|
|
27
|
+
"source": {
|
|
28
|
+
"kind": "url",
|
|
29
|
+
"url": "https://github.com/receptron/mulmocast-media/raw/refs/heads/main/soundeffects/opengameart/ezduzziteh-boings/bing_01.mp3"
|
|
30
|
+
},
|
|
31
|
+
"startAt": 0.8
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"source": {
|
|
35
|
+
"kind": "url",
|
|
36
|
+
"url": "https://github.com/receptron/mulmocast-media/raw/refs/heads/main/soundeffects/opengameart/junkala-512-retro-sfx/general-sounds/coins/sfx_coin_single1.wav"
|
|
37
|
+
},
|
|
38
|
+
"startAt": 2.2,
|
|
39
|
+
"volume": 0.7
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"text": "パチーン! 大正解です。",
|
|
45
|
+
"image": { "type": "textSlide", "slide": { "title": "大正解" } },
|
|
46
|
+
"soundEffects": [
|
|
47
|
+
{
|
|
48
|
+
"source": {
|
|
49
|
+
"kind": "url",
|
|
50
|
+
"url": "https://github.com/receptron/mulmocast-media/raw/refs/heads/main/soundeffects/kenney/impact-sounds/impactPunch_medium_000.ogg"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"source": {
|
|
55
|
+
"kind": "url",
|
|
56
|
+
"url": "https://github.com/receptron/mulmocast-media/raw/refs/heads/main/soundeffects/opengameart/junkala-512-retro-sfx/general-sounds/fanfares/sfx_sounds_fanfare1.wav"
|
|
57
|
+
},
|
|
58
|
+
"startAt": 0.6,
|
|
59
|
+
"volume": 0.8
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
}
|
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$mulmocast": {
|
|
3
|
+
"version": "1.1"
|
|
4
|
+
},
|
|
5
|
+
"lang": "ja",
|
|
6
|
+
"title": "かぞえてみよう(効果音とアニメーションの同期サンプル)",
|
|
7
|
+
"description": "html_tailwind animations and beat sound effects (JingleScript scores and a URL) placed on the same times; no image generation.",
|
|
8
|
+
"audioParams": {
|
|
9
|
+
"bgmVolume": 0.04
|
|
10
|
+
},
|
|
11
|
+
"beats": [
|
|
12
|
+
{
|
|
13
|
+
"text": "こんにちは! きょうは、いっしょに かずを かぞえてみよう!",
|
|
14
|
+
"image": {
|
|
15
|
+
"type": "html_tailwind",
|
|
16
|
+
"html": [
|
|
17
|
+
"<div class='h-full w-full flex items-center justify-center bg-gradient-to-b from-sky-300 to-sky-100 overflow-hidden'>",
|
|
18
|
+
" <h1 id='title' class='text-8xl font-black text-orange-500 drop-shadow-lg' style='transform:translateY(-500px)'>かぞえてみよう!</h1>",
|
|
19
|
+
"</div>"
|
|
20
|
+
],
|
|
21
|
+
"script": [
|
|
22
|
+
"const seg = (t, a, b) => Math.max(0, Math.min(1, (t - a) / (b - a)));",
|
|
23
|
+
"function render(frame, totalFrames, fps) {",
|
|
24
|
+
" const t = frame / fps;",
|
|
25
|
+
" // Falls from 0.3 s and lands at 0.8 s (the 'hit' cue of the jingle), then bounces back from a squash.",
|
|
26
|
+
" const y = -500 * (1 - Easing.easeIn(seg(t, 0.3, 0.8)));",
|
|
27
|
+
" const scale = t < 0.8 ? 1 : 1.25 - 0.25 * Easing.easeOut(seg(t, 0.8, 1.1));",
|
|
28
|
+
" document.getElementById('title').style.transform = `translateY(${y}px) scale(${scale})`;",
|
|
29
|
+
"}"
|
|
30
|
+
],
|
|
31
|
+
"animation": true
|
|
32
|
+
},
|
|
33
|
+
"soundEffects": [
|
|
34
|
+
{
|
|
35
|
+
"source": {
|
|
36
|
+
"kind": "jinglescript",
|
|
37
|
+
"score": {
|
|
38
|
+
"format": "jinglescript/1",
|
|
39
|
+
"title": "Title lands",
|
|
40
|
+
"tempo": 120,
|
|
41
|
+
"length": {
|
|
42
|
+
"seconds": 3
|
|
43
|
+
},
|
|
44
|
+
"cues": {
|
|
45
|
+
"hit": {
|
|
46
|
+
"seconds": 0.8
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"tracks": [
|
|
50
|
+
{
|
|
51
|
+
"instrument": "whoosh",
|
|
52
|
+
"gain": -4,
|
|
53
|
+
"notes": [
|
|
54
|
+
{
|
|
55
|
+
"end": "hit",
|
|
56
|
+
"len": {
|
|
57
|
+
"seconds": 0.5
|
|
58
|
+
},
|
|
59
|
+
"variant": "fast"
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"instrument": "impact",
|
|
65
|
+
"gain": -6,
|
|
66
|
+
"notes": [
|
|
67
|
+
{
|
|
68
|
+
"at": "hit",
|
|
69
|
+
"variant": "soft"
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"instrument": "marimba",
|
|
75
|
+
"notes": [
|
|
76
|
+
{
|
|
77
|
+
"at": "hit",
|
|
78
|
+
"pitch": ["C5", "E5", "G5", "C6"],
|
|
79
|
+
"vel": 1.0
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"at": "hit+1",
|
|
83
|
+
"pitch": "C6",
|
|
84
|
+
"vel": 0.35
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"instrument": "marimba",
|
|
90
|
+
"notes": [
|
|
91
|
+
{
|
|
92
|
+
"at": "hit",
|
|
93
|
+
"pitch": ["C3", "G3"],
|
|
94
|
+
"vel": 0.9
|
|
95
|
+
}
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
"volume": 0.8
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"text": "ほしが でてくるよ。ひとつ、ふたつ、みっつ! ぜんぶで みっつ だね。",
|
|
107
|
+
"image": {
|
|
108
|
+
"type": "html_tailwind",
|
|
109
|
+
"html": [
|
|
110
|
+
"<div class='h-full w-full flex flex-col items-center justify-center bg-indigo-950'>",
|
|
111
|
+
" <div class='flex gap-24'>",
|
|
112
|
+
" <div class='flex flex-col items-center'>",
|
|
113
|
+
" <div id='star0' class='w-48 h-48' style='transform:scale(0)'><svg viewBox='0 0 100 100' class='w-full h-full'><polygon points='50,5 61,38 96,38 68,59 79,93 50,72 21,93 32,59 4,38 39,38' fill='#facc15' stroke='#f59e0b' stroke-width='4' stroke-linejoin='round'/></svg></div>",
|
|
114
|
+
" <div id='num0' class='text-7xl font-black text-white mt-6' style='opacity:0'>1</div>",
|
|
115
|
+
" </div>",
|
|
116
|
+
" <div class='flex flex-col items-center'>",
|
|
117
|
+
" <div id='star1' class='w-48 h-48' style='transform:scale(0)'><svg viewBox='0 0 100 100' class='w-full h-full'><polygon points='50,5 61,38 96,38 68,59 79,93 50,72 21,93 32,59 4,38 39,38' fill='#facc15' stroke='#f59e0b' stroke-width='4' stroke-linejoin='round'/></svg></div>",
|
|
118
|
+
" <div id='num1' class='text-7xl font-black text-white mt-6' style='opacity:0'>2</div>",
|
|
119
|
+
" </div>",
|
|
120
|
+
" <div class='flex flex-col items-center'>",
|
|
121
|
+
" <div id='star2' class='w-48 h-48' style='transform:scale(0)'><svg viewBox='0 0 100 100' class='w-full h-full'><polygon points='50,5 61,38 96,38 68,59 79,93 50,72 21,93 32,59 4,38 39,38' fill='#facc15' stroke='#f59e0b' stroke-width='4' stroke-linejoin='round'/></svg></div>",
|
|
122
|
+
" <div id='num2' class='text-7xl font-black text-white mt-6' style='opacity:0'>3</div>",
|
|
123
|
+
" </div>",
|
|
124
|
+
" </div>",
|
|
125
|
+
"</div>"
|
|
126
|
+
],
|
|
127
|
+
"script": [
|
|
128
|
+
"const times = [0.8, 1.6, 2.4];",
|
|
129
|
+
"const seg = (t, a, b) => Math.max(0, Math.min(1, (t - a) / (b - a)));",
|
|
130
|
+
"function render(frame, totalFrames, fps) {",
|
|
131
|
+
" const t = frame / fps;",
|
|
132
|
+
" times.forEach((at, i) => {",
|
|
133
|
+
" // Pops out at its time (overshooting to 1.25), then settles to 1.",
|
|
134
|
+
" const scale = t < at ? 0 : t < at + 0.12 ? 1.25 * Easing.easeOut(seg(t, at, at + 0.12)) : 1.25 - 0.25 * Easing.easeInOut(seg(t, at + 0.12, at + 0.3));",
|
|
135
|
+
" document.getElementById(`star${i}`).style.transform = `scale(${scale})`;",
|
|
136
|
+
" const shown = Easing.easeOut(seg(t, at, at + 0.25));",
|
|
137
|
+
" const num = document.getElementById(`num${i}`);",
|
|
138
|
+
" num.style.opacity = shown;",
|
|
139
|
+
" num.style.transform = `translateY(${20 * (1 - shown)}px)`;",
|
|
140
|
+
" });",
|
|
141
|
+
"}"
|
|
142
|
+
],
|
|
143
|
+
"animation": true
|
|
144
|
+
},
|
|
145
|
+
"soundEffects": [
|
|
146
|
+
{
|
|
147
|
+
"source": {
|
|
148
|
+
"kind": "jinglescript",
|
|
149
|
+
"score": {
|
|
150
|
+
"format": "jinglescript/1",
|
|
151
|
+
"title": "Three pops",
|
|
152
|
+
"tempo": 120,
|
|
153
|
+
"length": {
|
|
154
|
+
"seconds": 3.2
|
|
155
|
+
},
|
|
156
|
+
"tracks": [
|
|
157
|
+
{
|
|
158
|
+
"instrument": "pop",
|
|
159
|
+
"reverb": false,
|
|
160
|
+
"notes": [
|
|
161
|
+
{
|
|
162
|
+
"at": {
|
|
163
|
+
"seconds": 0.8
|
|
164
|
+
},
|
|
165
|
+
"pitch": "E6",
|
|
166
|
+
"vel": 0.85
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"at": {
|
|
170
|
+
"seconds": 1.6
|
|
171
|
+
},
|
|
172
|
+
"pitch": "G6",
|
|
173
|
+
"vel": 0.85
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
"at": {
|
|
177
|
+
"seconds": 2.4
|
|
178
|
+
},
|
|
179
|
+
"pitch": "C7",
|
|
180
|
+
"vel": 0.85
|
|
181
|
+
}
|
|
182
|
+
]
|
|
183
|
+
}
|
|
184
|
+
]
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
]
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
"text": "ボールが はねるよ。ぽーん、ぽーん、ぽーん。なんかい はねたかな?",
|
|
192
|
+
"image": {
|
|
193
|
+
"type": "html_tailwind",
|
|
194
|
+
"html": [
|
|
195
|
+
"<div class='h-full w-full relative bg-gradient-to-b from-amber-50 to-amber-100 overflow-hidden'>",
|
|
196
|
+
" <div class='absolute left-0 right-0 bottom-0 h-24 bg-green-500'></div>",
|
|
197
|
+
" <div id='ball' class='absolute w-32 h-32 rounded-full bg-gradient-to-br from-rose-400 to-red-600 shadow-xl' style='left:calc(50% - 64px); bottom:96px'></div>",
|
|
198
|
+
"</div>"
|
|
199
|
+
],
|
|
200
|
+
"script": [
|
|
201
|
+
"const landings = [0.6, 1.2, 1.8];",
|
|
202
|
+
"const floor = 96, peak = 520; // (not `top`: that is window.top)",
|
|
203
|
+
"function render(frame, totalFrames, fps) {",
|
|
204
|
+
" const t = frame / fps;",
|
|
205
|
+
" let h = 0;",
|
|
206
|
+
" if (t < landings[0]) {",
|
|
207
|
+
" const u = t / landings[0];",
|
|
208
|
+
" h = peak * (1 - u * u); // falling from the top",
|
|
209
|
+
" } else {",
|
|
210
|
+
" for (let k = 1; k < landings.length; k++) {",
|
|
211
|
+
" if (t < landings[k]) {",
|
|
212
|
+
" const u = (t - landings[k - 1]) / (landings[k] - landings[k - 1]);",
|
|
213
|
+
" h = peak * Math.pow(0.5, k) * 4 * u * (1 - u); // smaller arc after each bounce",
|
|
214
|
+
" break;",
|
|
215
|
+
" }",
|
|
216
|
+
" }",
|
|
217
|
+
" }",
|
|
218
|
+
" const ball = document.getElementById('ball');",
|
|
219
|
+
" ball.style.bottom = `${floor + h}px`;",
|
|
220
|
+
" // Squash for a moment on each landing.",
|
|
221
|
+
" const squash = landings.some((l) => t >= l && t < l + 0.08);",
|
|
222
|
+
" ball.style.transform = squash ? 'scale(1.25, 0.75) translateY(10px)' : 'none';",
|
|
223
|
+
"}"
|
|
224
|
+
],
|
|
225
|
+
"animation": true
|
|
226
|
+
},
|
|
227
|
+
"soundEffects": [
|
|
228
|
+
{
|
|
229
|
+
"source": {
|
|
230
|
+
"kind": "url",
|
|
231
|
+
"url": "https://github.com/receptron/mulmocast-media/raw/refs/heads/main/soundeffects/opengameart/ezduzziteh-boings/bing_01.mp3"
|
|
232
|
+
},
|
|
233
|
+
"startAt": 0.6,
|
|
234
|
+
"volume": 0.9
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
"source": {
|
|
238
|
+
"kind": "url",
|
|
239
|
+
"url": "https://github.com/receptron/mulmocast-media/raw/refs/heads/main/soundeffects/opengameart/ezduzziteh-boings/bing_01.mp3"
|
|
240
|
+
},
|
|
241
|
+
"startAt": 1.2,
|
|
242
|
+
"volume": 0.7
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
"source": {
|
|
246
|
+
"kind": "url",
|
|
247
|
+
"url": "https://github.com/receptron/mulmocast-media/raw/refs/heads/main/soundeffects/opengameart/ezduzziteh-boings/bing_01.mp3"
|
|
248
|
+
},
|
|
249
|
+
"startAt": 1.8,
|
|
250
|
+
"volume": 0.5
|
|
251
|
+
}
|
|
252
|
+
]
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
"text": "とけいの はりが うごくよ。チク、タク、チク、タク……チーン! いっしゅう したね。",
|
|
256
|
+
"image": {
|
|
257
|
+
"type": "html_tailwind",
|
|
258
|
+
"html": [
|
|
259
|
+
"<div class='h-full w-full flex items-center justify-center bg-teal-100'>",
|
|
260
|
+
" <svg id='clock' viewBox='0 0 200 200' class='w-96 h-96'>",
|
|
261
|
+
" <circle cx='100' cy='100' r='92' fill='white' stroke='#0f766e' stroke-width='10'/>",
|
|
262
|
+
" <g id='marks'></g>",
|
|
263
|
+
" <line id='hand' x1='100' y1='100' x2='100' y2='30' stroke='#e11d48' stroke-width='8' stroke-linecap='round'/>",
|
|
264
|
+
" <circle cx='100' cy='100' r='8' fill='#0f766e'/>",
|
|
265
|
+
" </svg>",
|
|
266
|
+
"</div>"
|
|
267
|
+
],
|
|
268
|
+
"script": [
|
|
269
|
+
"const marks = document.getElementById('marks');",
|
|
270
|
+
"for (let i = 0; i < 6; i++) {",
|
|
271
|
+
" const a = (i * 60 * Math.PI) / 180;",
|
|
272
|
+
" marks.innerHTML += `<circle cx='${100 + 74 * Math.sin(a)}' cy='${100 - 74 * Math.cos(a)}' r='6' fill='#14b8a6'/>`;",
|
|
273
|
+
"}",
|
|
274
|
+
"function render(frame, totalFrames, fps) {",
|
|
275
|
+
" const t = frame / fps;",
|
|
276
|
+
" // One step every 0.5 s: the tick at 0.5 ... 2.5 s, the full turn (and the chime) at 3.0 s.",
|
|
277
|
+
" const steps = Math.min(Math.floor(t / 0.5 + 1e-6), 6);",
|
|
278
|
+
" document.getElementById('hand').setAttribute('transform', `rotate(${steps * 60} 100 100)`);",
|
|
279
|
+
" const shaking = t >= 3.0 && t < 3.6;",
|
|
280
|
+
" const angle = shaking ? 8 * Math.sin((t - 3.0) * 40) * (1 - (t - 3.0) / 0.6) : 0;",
|
|
281
|
+
" document.getElementById('clock').style.transform = `rotate(${angle}deg)`;",
|
|
282
|
+
"}"
|
|
283
|
+
],
|
|
284
|
+
"animation": true
|
|
285
|
+
},
|
|
286
|
+
"soundEffects": [
|
|
287
|
+
{
|
|
288
|
+
"source": {
|
|
289
|
+
"kind": "jinglescript",
|
|
290
|
+
"score": {
|
|
291
|
+
"format": "jinglescript/1",
|
|
292
|
+
"title": "Clock turn",
|
|
293
|
+
"tempo": 120,
|
|
294
|
+
"length": {
|
|
295
|
+
"seconds": 4.5
|
|
296
|
+
},
|
|
297
|
+
"cues": {
|
|
298
|
+
"first": {
|
|
299
|
+
"seconds": 0.5
|
|
300
|
+
},
|
|
301
|
+
"hit": {
|
|
302
|
+
"seconds": 3.0
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
"tracks": [
|
|
306
|
+
{
|
|
307
|
+
"instrument": "clock",
|
|
308
|
+
"reverb": false,
|
|
309
|
+
"notes": [
|
|
310
|
+
{
|
|
311
|
+
"at": "first",
|
|
312
|
+
"variant": ["tick", "tock"],
|
|
313
|
+
"vel": 0.8,
|
|
314
|
+
"repeat": {
|
|
315
|
+
"every": 1,
|
|
316
|
+
"count": 5
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
]
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
"instrument": "glockenspiel",
|
|
323
|
+
"notes": [
|
|
324
|
+
{
|
|
325
|
+
"at": "hit",
|
|
326
|
+
"pitch": ["C6", "E6", "G6"],
|
|
327
|
+
"vel": 0.9
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
"at": "hit+1",
|
|
331
|
+
"pitch": "C7",
|
|
332
|
+
"vel": 0.4
|
|
333
|
+
}
|
|
334
|
+
]
|
|
335
|
+
}
|
|
336
|
+
]
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
]
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
"text": "さいごの もんだい。ほしは いくつ でたかな? そう、みっつ! せいかい! よくできました!",
|
|
344
|
+
"image": {
|
|
345
|
+
"type": "html_tailwind",
|
|
346
|
+
"html": [
|
|
347
|
+
"<div class='h-full w-full flex items-center justify-center bg-yellow-50'>",
|
|
348
|
+
" <div id='stamp' class='w-[420px] h-[420px] rounded-full border-[28px] border-red-500 flex items-center justify-center' style='opacity:0'>",
|
|
349
|
+
" <span class='text-8xl font-black text-red-500'>せいかい</span>",
|
|
350
|
+
" </div>",
|
|
351
|
+
"</div>"
|
|
352
|
+
],
|
|
353
|
+
"script": [
|
|
354
|
+
"const seg = (t, a, b) => Math.max(0, Math.min(1, (t - a) / (b - a)));",
|
|
355
|
+
"function render(frame, totalFrames, fps) {",
|
|
356
|
+
" const t = frame / fps;",
|
|
357
|
+
" // Comes down from 0.7 s and hits the paper at 1.0 s, with a small rebound.",
|
|
358
|
+
" const down = Easing.easeIn(seg(t, 0.7, 1.0));",
|
|
359
|
+
" const rebound = t < 1.0 ? 0 : t < 1.08 ? 0.08 * seg(t, 1.0, 1.08) : 0.08 * (1 - Easing.easeInOut(seg(t, 1.08, 1.25)));",
|
|
360
|
+
" const stamp = document.getElementById('stamp');",
|
|
361
|
+
" stamp.style.opacity = seg(t, 0.7, 0.8);",
|
|
362
|
+
" stamp.style.transform = `scale(${3 - 2 * down + rebound}) rotate(${-25 + 17 * down}deg)`;",
|
|
363
|
+
"}"
|
|
364
|
+
],
|
|
365
|
+
"animation": true
|
|
366
|
+
},
|
|
367
|
+
"soundEffects": [
|
|
368
|
+
{
|
|
369
|
+
"source": {
|
|
370
|
+
"kind": "jinglescript",
|
|
371
|
+
"score": {
|
|
372
|
+
"format": "jinglescript/1",
|
|
373
|
+
"title": "Correct!",
|
|
374
|
+
"tempo": 132,
|
|
375
|
+
"length": {
|
|
376
|
+
"seconds": 4
|
|
377
|
+
},
|
|
378
|
+
"cues": {
|
|
379
|
+
"hit": {
|
|
380
|
+
"seconds": 1.0
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
"tracks": [
|
|
384
|
+
{
|
|
385
|
+
"instrument": "impact",
|
|
386
|
+
"gain": -3,
|
|
387
|
+
"notes": [
|
|
388
|
+
{
|
|
389
|
+
"at": "hit",
|
|
390
|
+
"variant": "hard"
|
|
391
|
+
}
|
|
392
|
+
]
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
"instrument": "marimba",
|
|
396
|
+
"notes": [
|
|
397
|
+
{
|
|
398
|
+
"at": "hit+1",
|
|
399
|
+
"pitch": "C5",
|
|
400
|
+
"vel": 0.8
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
"at": "hit+1.5",
|
|
404
|
+
"pitch": "E5",
|
|
405
|
+
"vel": 0.85
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
"at": "hit+2",
|
|
409
|
+
"pitch": "G5",
|
|
410
|
+
"vel": 0.9
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
"at": "hit+2.5",
|
|
414
|
+
"pitch": ["C5", "E5", "G5", "C6"],
|
|
415
|
+
"vel": 1.0
|
|
416
|
+
}
|
|
417
|
+
]
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
"instrument": "clap",
|
|
421
|
+
"gain": -6,
|
|
422
|
+
"notes": [
|
|
423
|
+
{
|
|
424
|
+
"at": "hit+2.5",
|
|
425
|
+
"variant": "group",
|
|
426
|
+
"repeat": {
|
|
427
|
+
"every": 0.5,
|
|
428
|
+
"count": 6
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
]
|
|
432
|
+
}
|
|
433
|
+
]
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
"volume": 0.8
|
|
437
|
+
}
|
|
438
|
+
]
|
|
439
|
+
}
|
|
440
|
+
]
|
|
441
|
+
}
|