Created
October 23, 2025 19:28
-
-
Save penbuvt/6179bf3fa9f1dfb40a6f5842558d28bb to your computer and use it in GitHub Desktop.
Karaoke template for "Butter-Fly" ft. Nice Eggy & Kip Cintamani: https://youtu.be/t35Zu71p6og
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| %;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| %;;;; Penbu-standard vtuber karaoke clip k-template | |
| %;;;; Eggy & Kip ver. | |
| %;;;; | |
| Karaoke template by Penbu VT, originally made for the karaoke duet edit | |
| featuring Nice Eggy and Kip Cintamani singing "Butter-Fly" by Wada Kouji. | |
| https://youtu.be/t35Zu71p6og | |
| Written for aegsc v0.39.0: | |
| https://github.com/butterfansubs/aegsc | |
| Output compatible with The0x539's Templater: | |
| https://github.com/The0x539/Aegisub-Scripts/blob/trunk/src/0x.KaraTemplater.moon | |
| %;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| %;;; License | |
| %;;; | |
| %; SPDX-License-Identifier: MIT | |
| MIT License | |
| Copyright (c) 2025 Penbu | |
| Permission is hereby granted, free of charge, to any person obtaining a copy | |
| of this software and associated documentation files (the "Software"), to deal | |
| in the Software without restriction, including without limitation the rights | |
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| copies of the Software, and to permit persons to whom the Software is | |
| furnished to do so, subject to the following conditions: | |
| The above copyright notice and this permission notice shall be included in all | |
| copies or substantial portions of the Software. | |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| SOFTWARE. | |
| %;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| %;;; Preamble | |
| %;;; | |
| %[ code once | |
| -- rounds n to 3 decimal places | |
| function r(n) | |
| return util.ftoa(n, 3) | |
| end | |
| -- rounds n (in milliseconds) to the nearest centisecond | |
| function cs(n) | |
| return math.floor(n / 10 + 0.5) * 10 | |
| end | |
| %] | |
| %! code once | outer_bord_size = 7.875 | |
| %[ code once | |
| -- Video dimensions. | |
| -- There's probably a way to get this from the script itself | |
| vw = 1280 | |
| vh = 720 | |
| %] | |
| %[ code once | |
| -- 165 bpm | |
| beat = 60 / 165 * 1000 | |
| lead_in = cs(beat) | |
| -- Utility to shift a time back to its original position | |
| -- after the line has been retimed to add lead-in | |
| function t(n) | |
| return n + lead_in | |
| end | |
| %] | |
| %[ code once anystyle | |
| speaker_colors = { | |
| ["Chat"] = "&H808080&", | |
| ["NiceEggy"] = "&H99E3FE&", | |
| ["KipCintamani"] = "&HC6D59B&" | |
| } | |
| function speaker_c(speaker) | |
| return speaker:find(speaker) | |
| and speaker_colors[speaker] | |
| or "&H808080&" | |
| end | |
| %] | |
| %[ code once | |
| -- Tiresias Infofont has incorrect metrics | |
| -- and causes extra space to appear when using orgline.left and orgline.right | |
| adjustment_factor = 0.19 | |
| function calc_padding(left, right, factor) | |
| return (right - left) * factor / 2 | |
| end | |
| %] | |
| %[ code line | |
| padding = calc_padding(orgline.left, orgline.right, adjustment_factor) | |
| left = orgline.left + padding - outer_bord_size | |
| right = orgline.right - padding + outer_bord_size | |
| -- The lead-out effect depends on the next line, | |
| -- so gather info about the next line. | |
| if not orgline.next then -- last line | |
| continuous_end = false | |
| next_start = orgline.end_time - orgline.start_time | |
| next_right = right | |
| elseif orgline.end_time == orgline.next.start_time then -- continuous with next | |
| continuous_end = true | |
| next_start = orgline.next.start_time - orgline.start_time | |
| next_right = orgline.next.right | |
| - calc_padding(orgline.next.left, orgline.next.right, adjustment_factor) | |
| + outer_bord_size | |
| else -- not continuous | |
| continuous_end = false | |
| next_start = orgline.end_time - orgline.start_time + lead_in | |
| next_right = right | |
| end | |
| -- Vertical clip boundaries by layer | |
| y1 = { | |
| 0, | |
| 0, | |
| orgline.middle, | |
| 0, | |
| } | |
| y2 = { | |
| vh, | |
| orgline.middle, | |
| vh, | |
| vh, | |
| } | |
| %] | |
| %;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| %;;; Main template | |
| %;;; | |
| Layer 1: Pre-syl outer border | |
| Layer 2: Post-syl outer outline, top half | |
| Layer 3: Post-syl outer outline, bottom half | |
| Layer 4: Foreground text (inner outline, fill, shadow) | |
| %[ template line loop bg 3 @1 | |
| !relayer($loop_bg)! | |
| { | |
| \pos(!orgline.x!,!orgline.y!) | |
| \an2 | |
| \be1 | |
| \bord!outer_bord_size! | |
| \shad0 | |
| \1c!speaker_c("")! | |
| \3c!speaker_c("")! | |
| } | |
| %] | |
| %[ template line @4 | |
| { | |
| \pos(!orgline.x!,!orgline.y!) | |
| \an2 | |
| \bord3.125 | |
| \3c&H000000& | |
| } | |
| %] | |
| %;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| %;;; Lead-in | |
| %;;; | |
| The lead-in effect is a 1-beat long wipe from left to right, timed | |
| so that the line is fully visible at the start time of the line. | |
| %! mixin line | !retime("line", -lead_in, 0)! | |
| %[ mixin line layer 1 | |
| { | |
| \clip( | |
| !r(left - outer_bord_size)!,!y1[line.layer]!, | |
| !r(right) - vw!,!y2[line.layer]! | |
| ) | |
| \t(0,!lead_in!, | |
| \clip( | |
| !r(left - outer_bord_size)!,!y1[line.layer]!, | |
| !r(right)!,!y2[line.layer]! | |
| ) | |
| ) | |
| \t(!lead_in!,!lead_in * 2!, | |
| \clip( | |
| !r(left - outer_bord_size)!,!y1[line.layer]!, | |
| !r(right) + vw!,!y2[line.layer]! | |
| ) | |
| )} | |
| %] | |
| %[ mixin line layer 4 | |
| { | |
| \clip( | |
| !r(next_right) - vw!,!y1[line.layer]!, | |
| !r(right) - vw!,!y2[line.layer]! | |
| ) | |
| \t(0,!lead_in!, | |
| \clip( | |
| !r(next_right) - vw!,!y1[line.layer]!, | |
| !r(right)!,!y2[line.layer]! | |
| ) | |
| ) | |
| \t(!lead_in!,!lead_in * 2!, | |
| \clip( | |
| !r(next_right) - vw!,!y1[line.layer]!, | |
| !r(right) + vw!,!y2[line.layer]! | |
| ) | |
| )} | |
| %] | |
| %;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| %;;; syl effect | |
| %;;; | |
| The syl effect is similar to \kf, acting only on the outer outline. | |
| The foreground text transitions from 50% opacity to 100% opacity by syl, | |
| taking the duration of a 16th note to complete. | |
| %;; Removing the pre-syl outer outline, \kf-style | |
| %[ mixin syl prefix layer 1 | |
| { | |
| \t(!t(syl.start_time)!,!t(syl.end_time)!, | |
| \clip( | |
| !r(left + outer_bord_size * 2 + syl.right * (1 - adjustment_factor))!,!y1[line.layer]!, | |
| !r(right)!,!y2[line.layer]! | |
| ) | |
| ) | |
| } | |
| %] | |
| %;; Uncovering the post-syl outlines, \kf-style | |
| %[ mixin line prefix layer 2 layer 3 | |
| { | |
| \clip( | |
| !r(next_right) - vw!,!y1[line.layer]!, | |
| !r(left - outer_bord_size)!,!y2[line.layer]! | |
| ) | |
| } | |
| %] | |
| %[ mixin syl prefix layer 2 layer 3 | |
| { | |
| \t(!t(syl.start_time)!,!t(syl.end_time)!, | |
| \clip( | |
| !r(next_right) - vw!,!y1[line.layer]!, | |
| !r(left + outer_bord_size * 2 + syl.right * (1 - adjustment_factor))!,!y2[line.layer]! | |
| ) | |
| ) | |
| } | |
| %] | |
| %;; Post-syl colours | |
| If only one singer is singing, both halves have the same colour. | |
| When both singers are singing, each half has a different colour. | |
| %[ mixin line layer 2 layer 3 actor NiceEggy actor KipCintamani | |
| { | |
| \1c!speaker_c(orgline.actor)! | |
| \3c!speaker_c(orgline.actor)! | |
| } | |
| %] | |
| %[ mixin line layer 2 actor Both | |
| { | |
| \1c!speaker_c("NiceEggy")! | |
| \3c!speaker_c("NiceEggy")! | |
| } | |
| %] | |
| %[ mixin line layer 3 actor Both | |
| { | |
| \1c!speaker_c("KipCintamani")! | |
| \3c!speaker_c("KipCintamani")! | |
| } | |
| %] | |
| %;; Foreground syl effect | |
| %[ mixin syl layer 4 | |
| { | |
| \shad0 | |
| \1a&H80& | |
| \3a&H80& | |
| \4a&HFF& | |
| \t(!t(syl.start_time)!,!t(syl.start_time) + r(beat / 4)!, | |
| \1a&H00& | |
| \3a&H00& | |
| \4a&H96& | |
| \shad0.625 | |
| ) | |
| } | |
| %] | |
| %;; Fall effect | |
| Special effect for when Kip stumbles on one of the lines: | |
| the rest of the line falls away when the marked syl ends. | |
| Implemented as the \-fall syl fx. | |
| %[ mixin syl sylfx fall | |
| { | |
| \t(!t(syl.end_time)!,!t(syl.end_time + lead_in)!,4, | |
| \frz-90 | |
| ) | |
| } | |
| %] | |
| %;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| %;;; Lead-out | |
| %;;; | |
| The lead-out effect is a wipe matching the position and timing of the | |
| lead-in of the next line. | |
| %; The0x539's Templater at commit dcee98d does not have `mixin line suffix`, | |
| %; so work around it by hooking into the last syl instead. | |
| %; This is important because the final value for a tag transformed by \t | |
| %; is solely determined by the last \t affecting that tag regardless of its timing, | |
| %; so the \clip with the final coordinates MUST be last. | |
| %[ code once | |
| function last_syl() | |
| return not syl.next | |
| end | |
| %] | |
| %; Extend the end to accomodate the lead-out effect | |
| %! mixin line | !retime("delta", 0, lead_in)! | |
| %[ mixin syl prefix if last_syl \ | |
| layer 2 layer 3 layer 4 %; layer 1 is ignored to avoid issues with stacked \t effects. | |
| %; It should already be fully invisible by the time the wipe catches up. | |
| { | |
| \t(!t(next_start - lead_in)!,!t(next_start)!, | |
| \clip( | |
| !r(next_right)!,!y1[line.layer]!, | |
| !r(right)!,!y2[line.layer]! | |
| ) | |
| ) | |
| \t(!t(next_start)!,!t(next_start + lead_in)!, | |
| \clip( | |
| !r(next_right) + vw!,!y1[line.layer]!, | |
| !r(right) + vw!,!y2[line.layer]! | |
| ) | |
| )} | |
| %] | |
| %;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| %;;; Speech | |
| %;;; | |
| The speech style is the post-syl style but without any fancy animations | |
| or retimes. Chat lines read out by the streamer are italicized. | |
| %!!set-defaults, $ Speech | |
| %[ template line | |
| { | |
| \pos(!orgline.x!,!orgline.y!) | |
| \an2 | |
| \be1 | |
| \bord!outer_bord_size! | |
| \shad0 | |
| \1c!speaker_c(orgline.actor)! | |
| \3c!speaker_c(orgline.actor)! | |
| } | |
| %] | |
| %[ template line @1 | |
| { | |
| \pos(!orgline.x!,!orgline.y!) | |
| \an2 | |
| \bord3.125 | |
| \3c&H000000& | |
| } | |
| %] | |
| %! mixin line actor Chat | {\i1} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment