Complete rewrite from scratch

- Plain JS
- BS+ only
- Simple song info only
- No configuration
This commit is contained in:
Isaiah Billingsley 2026-02-19 08:47:37 +00:00
commit 63361cf5d1
19 changed files with 506 additions and 0 deletions

9
.editorconfig Normal file
View File

@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = tab
max_line_length = 120
insert_final_newline = true
trim_trailing_whitespace = true

12
LICENSE.txt Normal file
View File

@ -0,0 +1,12 @@
Copyright (c) 2025 iza
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.

7
README.md Normal file
View File

@ -0,0 +1,7 @@
# Beat Saber Overlay
Simple Beat Saber stream overlay for https://www.twitch.tv/iza_k
Requires BeatSaberPlus
<img width="533" height="95" alt="image" src="https://github.com/user-attachments/assets/7c334ac5-8ed1-4dfd-b2a6-3da682da4357" />

18
dprint.json Normal file
View File

@ -0,0 +1,18 @@
{
"lineWidth": 120,
"newLineKind": "lf",
"useTabs": true,
"typescript": { "useBraces": "maintain" },
"json": {},
"markdown": {},
"malva": {},
"markup": {},
"excludes": ["**/node_modules", "**/build", "**/dist", "**/*-lock.json", "**/*.svg"],
"plugins": [
"https://plugins.dprint.dev/typescript-0.95.15.wasm",
"https://plugins.dprint.dev/json-0.21.1.wasm",
"https://plugins.dprint.dev/markdown-0.21.1.wasm",
"https://plugins.dprint.dev/g-plane/malva-v0.15.2.wasm",
"https://plugins.dprint.dev/g-plane/markup_fmt-v0.26.0.wasm"
]
}

BIN
fonts/montserrat.woff2 Normal file

Binary file not shown.

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 28.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
version="1.1"
id="Layer_1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px"
y="0px"
viewBox="0 0 1280 1280"
style="enable-background: new 0 0 1280 1280"
xml:space="preserve"
>
<style type="text/css">.st0{fill:#FFFFFF;}</style>
<g>
<path
class="st0"
d="M197.39,338.14v127.8H83V314.09c0-99.23,36.13-148.87,108.43-148.87h100.46
c72.28,0,108.43,49.63,108.43,148.87v202.98c0,62.16-13.93,100.25-41.77,114.28c27.84,14.03,41.77,50.12,41.77,108.26v225.55
c0,99.23-36.15,148.85-108.43,148.85H191.43C119.13,1114,83,1064.38,83,965.15V814.78h114.39v126.3c0,16.06,1.99,26.83,5.96,32.34
s11.27,8.27,21.89,8.27h33.82c10.6,0,17.9-2.76,21.88-8.27c3.97-5.51,5.98-16.28,5.98-32.34V738.1c0-16.03-1.83-27.05-5.48-33.07
s-11.1-9.03-22.37-9.03h-76.6V569.69h76.6c10.6,0,17.9-2.76,21.88-8.27c3.97-5.51,5.98-16.28,5.98-32.32V338.14
c0-16.03-2-26.81-5.98-32.32c-3.97-5.51-11.27-8.28-21.88-8.28h-33.82c-10.62,0-17.92,2.77-21.89,8.28S197.39,322.12,197.39,338.14
z"
/>
<path
class="st0"
d="M584.33,562.18h108.43c72.28,0,108.43,49.62,108.43,148.85v254.12c0,99.23-36.15,148.85-108.43,148.85h-113.4
c-72.29,0-108.43-49.62-108.43-148.85V314.09c0-99.23,36.13-148.87,108.43-148.87h108.43c72.28,0,108.43,49.63,108.43,148.87
v145.84h-112.4V338.14c0-16.03-2-26.81-5.98-32.32s-11.27-8.28-21.88-8.28h-43.77c-10.62,0-17.92,2.77-21.89,8.28
c-3.97,5.51-5.96,16.3-5.96,32.32L584.33,562.18L584.33,562.18z M688.78,941.08v-209c0-16.03-2-26.81-5.98-32.32
c-3.97-5.51-11.6-8.27-22.87-8.27h-75.6v249.59c0,16.06,1.99,26.83,5.96,32.34c3.97,5.51,11.27,8.27,21.89,8.27h47.75
c11.27,0,18.9-2.76,22.87-8.27S688.78,957.14,688.78,941.08z"
/>
<path
class="st0"
d="M1199.04,314.09v651.07c0,99.23-36.15,148.85-108.43,148.85H975.24c-72.29,0-108.43-49.62-108.43-148.85
V314.09c0-99.23,36.13-148.87,108.43-148.87h115.38C1162.9,165.22,1199.04,214.85,1199.04,314.09z M1084.66,941.08V338.14
c0-16.03-2-26.81-5.98-32.32s-11.6-8.28-22.87-8.28h-45.76c-10.62,0-17.92,2.77-21.89,8.28c-3.97,5.51-5.96,16.3-5.96,32.32v602.94
c0,16.06,1.99,26.83,5.96,32.34c3.97,5.51,11.27,8.27,21.89,8.27h45.76c11.27,0,18.9-2.76,22.87-8.27
S1084.66,957.14,1084.66,941.08z"
/>
</g>
<text
transform="matrix(1 0 0 1 -381.2729 778.7275)"
style='font-family: "MyriadPro-Regular"; font-size: 12px'
>Lorem ipsum</text>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1280 1280">
<g>
<path
fill="#FFFFFF"
d="M386.25,168.31h112.6c72.4,0,108.61,49.2,108.61,147.59v645.51c0,98.39-36.21,147.59-108.61,147.59H390.23
c-72.41,0-108.61-49.2-108.61-147.59V819.79h112.6v117.77c0,15.91,1.99,26.6,5.98,32.05c3.99,5.47,11.29,8.2,21.92,8.2h44.84
c10.62,0,17.94-2.73,21.92-8.2c3.99-5.45,5.98-16.14,5.98-32.05V730.34H386.25c-72.41,0-108.61-49.2-108.61-147.59V315.9
C277.64,217.51,313.84,168.31,386.25,168.31z M418.14,606.6h76.73V339.75c0-15.89-1.99-26.58-5.98-32.05
c-3.99-5.45-11.3-8.2-21.92-8.2h-48.83c-10.63,0-17.94,2.75-21.92,8.2c-3.99,5.47-5.98,16.17-5.98,32.05v226.6
c0,15.91,1.99,26.6,5.98,32.05C400.2,603.88,407.5,606.6,418.14,606.6z"
/>
<path
fill="#FFFFFF"
d="M1013,315.9v645.51c0,98.39-36.21,147.59-108.61,147.59H788.8c-72.41,0-108.61-49.2-108.61-147.59V315.9
c0-98.39,36.2-147.59,108.61-147.59h115.59C976.79,168.31,1013,217.51,1013,315.9z M898.41,937.56V339.75
c0-15.89-1.99-26.58-5.98-32.05c-3.98-5.45-11.63-8.2-22.92-8.2h-45.84c-10.63,0-17.94,2.75-21.92,8.2
c-3.98,5.47-5.98,16.17-5.98,32.05v597.81c0,15.91,1.99,26.6,5.98,32.05c3.99,5.47,11.29,8.2,21.92,8.2h45.84
c11.29,0,18.93-2.73,22.92-8.2C896.42,964.16,898.41,953.47,898.41,937.56z"
/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1280 1280">
<path
fill="#FFFFFF"
d="M639.63,931.84c-32.42,0-64.85,0.01-97.27,0c-49.35-0.02-91.47-40.08-94.1-89.41
c-0.23-4.24-0.68-8.49-1.24-12.7c-2.09-15.62-8.63-23.1-24.53-24.41c-22.41-1.84-43.94-5.51-62.46-19.39
c-25.42-19.04-40.52-43.98-39.39-76.17c0.66-18.65-4.31-34.83-11.99-51.32c-26.74-57.41-47.22-117.1-51.51-180.6
c-6.77-100.1,19.79-189.66,91.12-263.06c67.86-69.83,152.88-103.69,247.85-113.16c97.36-9.71,189.34,9.89,272.44,62.65
c101.84,64.65,154.46,157.75,155.77,279.06c0.74,69.03-18.74,133.48-44.27,196.55c-5.26,13-12.77,25.23-16.73,38.57
c-3.05,10.29-2.68,21.75-2.92,32.7c-0.47,22.04-6.94,41.84-20.71,59.07c-17.3,21.65-40.59,31.48-67.65,33.62
c-0.85,0.07-1.71-0.01-2.56,0.02c-27.51,1.05-35.9,8.81-37.05,36.03c-0.87,20.78-7.73,39.17-20.44,55.24
c-18.74,23.7-43.11,36.72-73.81,36.72C705.33,931.84,672.48,931.84,639.63,931.84z M480.05,675.84c54.01,0,96.15-41.51,96.22-94.81
c0.08-54.41-41.23-95.84-93.84-97.55c-53.66-1.74-99.41,45.06-98.41,95.82C385.11,633.95,425.78,675.84,480.05,675.84z
M800.21,483.84c-53.87,0-95.87,41.64-95.93,95.11c-0.06,54.3,41.34,95.6,94.14,97.25c53.21,1.66,99.2-44.9,98.12-96.11
C895.39,525.54,854.69,483.84,800.21,483.84z M640.62,775.21c3.39,5.64,6,11.05,9.59,15.72c9.19,11.96,24.81,16.36,37.63,11.13
c11.95-4.87,18.72-15.54,16.14-31.29c-6.07-37-23.41-67.85-53.77-90.78c-6.73-5.08-12.97-5.26-19.7-0.1
c-30.87,23.65-49.06,54.84-53.97,93.24c-1.86,14.55,4.56,24.54,16.05,28.77c12.63,4.65,24.61,2.59,34.06-7.2
C631.85,789.29,635.55,782.41,640.62,775.21z"
/>
<path
fill="#FFFFFF"
d="M1236.1,788.03L1236.1,788.03c-10.85-23.21-38.46-33.24-61.67-22.39l-533.92,249.47L106.75,766.76
c-23.23-10.81-50.82-0.74-61.63,22.49l0,0c-10.81,23.23-0.74,50.82,22.49,61.63l463.17,215.5l-12.54,5.86l-2.58-5.53L290.31,1172
l43.52,93.13l225.34-105.29l-1.66-3.55l83.09-38.82l82.58,38.42l-1.96,4.21l225.51,104.93l43.37-93.2l-225.51-104.93l-2.27,4.87
l-11.98-5.57l463.37-216.51C1236.92,838.85,1246.95,811.24,1236.1,788.03z"
/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
version="1.2"
baseProfile="tiny"
id="Layer_1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px"
y="0px"
viewBox="0 0 1280 1280"
overflow="visible"
xml:space="preserve"
>
<path
fill="#FFFFFF"
d="M589,559V243c0,0-1-13,11-13c9,0,67.64,0,82,0c14,0,11,15,11,15v415.45c0,0,2,6.55-20,20.55s-26,16-40,10
S356,574,356,574s-12-10-8-22s21-54,26-66s25-6,25-6L589,559z"
/>
<path
fill="#FFFFFF"
d="M1219.5,640c0-322.26-261.24-583.5-583.5-583.5S52.5,317.74,52.5,640s261.24,583.5,583.5,583.5
c73.98,0,144.73-13.78,209.85-38.89C877.96,1198.99,913.54,1207,951,1207c142.21,0,257.5-115.29,257.5-257.5
c0-39.06-8.71-76.08-24.27-109.24C1207.05,777.8,1219.5,710.36,1219.5,640z M739.36,1096.19c-32.16,7.08-65.57,10.81-99.86,10.81
C384.07,1107,177,899.93,177,644.5S384.07,182,639.5,182S1102,389.07,1102,644.5c0,30.9-3.04,61.09-8.82,90.3
C1052.44,707.76,1003.56,692,951,692c-142.21,0-257.5,115.29-257.5,257.5C693.5,1004.01,710.46,1054.56,739.36,1096.19z M1032,1099
H872v-61h160c0,0,37-26,37-69s-37-58-37-58H879v57l-101-98l101-98v68h151c0,0,102,21,102,130S1032,1099,1032,1099z"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1280 1280">
<path
fill="#FFFFFF"
d="M1214.77,1060.46L345.4,323.04c-10.53-8.93-26.3-7.63-35.23,2.89l0,0c-8.93,10.53-7.63,26.3,2.89,35.23
l760.31,644.9C1001.05,989.8,914.14,981,823,981c-112.48,0-218.54,13.4-298.65,37.72c-46.18,14.02-81.2,30.97-104.39,50.3
L231.57,407.7c-3.78-13.28-17.61-20.97-30.89-17.19c-13.28,3.78-20.97,17.61-17.19,30.89l200.62,704.3c0.58,2.03,1.39,3.92,2.4,5.67
c0.77,45.1,48.38,83.26,137.83,110.42c80.11,24.32,186.17,37.72,298.65,37.72s218.54-13.4,298.65-37.72
c90.18-27.38,137.85-65.95,137.85-111.53C1259.5,1104.5,1244.28,1080.98,1214.77,1060.46z M1107.71,1195.85
C1032,1218.84,930.88,1231.5,823,1231.5s-209-12.66-284.71-35.65c-71.7-21.77-103.79-48.63-103.79-65.6s32.09-43.83,103.79-65.6
C614,1041.66,715.12,1029,823,1029s209,12.66,284.71,35.65c28.28,8.59,50.4,17.97,66.82,27.22l16.4,13.91
c3,2.55,6.43,4.26,10.01,5.16c7.13,7.14,10.57,13.75,10.57,19.3C1211.5,1147.22,1179.41,1174.08,1107.71,1195.85z"
/>
<path
fill="#FFFFFF"
d="M314.38,157.41l-68.74-4.95l-63.09-91.22c-10.96-15.12-28.15-24.07-46.59-27.02
c-18.44-2.95-36.92,1.47-52.04,12.42l-41.81,30.3C10.9,99.56,3.9,143.36,26.52,174.58l58.98,83.44l-22.94,71
c-4.11,12.71,2.87,26.35,15.58,30.46h0c12.71,4.11,26.35-2.87,30.46-15.58l19.07-59.03l17.54,38.17c3.17,6.88,9.46,12.2,16.83,14.24
c2.25,0.62,4.53,0.93,6.8,0.93c5,0,9.91-1.48,14.12-4.36l85.76-58.73c8.29-6.13,12.84-7.92,15.24-13.3l0.98-2.22
c3.7-8.37,1.51-18.17-5.4-24.17l-1.31-1.13l-34.56-33.48l67.24,4.84c13.32,0.96,24.9-9.07,25.86-22.39v0
C337.73,169.95,327.71,158.37,314.38,157.41z M70.24,115.78l41.81-30.3c3.79-2.75,8.25-4.18,12.84-4.18c1.16,0,2.34,0.09,3.51,0.28
c5.79,0.92,10.88,4.05,14.32,8.8l50.4,73.24l-74.59,56.23l-53.19-73.4C58.24,136.64,60.44,122.88,70.24,115.78z M177.86,279.16
l-13.44-29.26l35.11-25l23.84,23.09L177.86,279.16z"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.1.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
version="1.2"
baseProfile="tiny"
id="Layer_1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px"
y="0px"
viewBox="0 0 1280 1280"
overflow="visible"
xml:space="preserve"
>
<path
fill="#FFFFFF"
d="M926.33,200H353.67C268.8,200,200,268.8,200,353.67v572.67c0,84.87,68.8,153.67,153.67,153.67h572.67
c84.87,0,153.67-68.8,153.67-153.67V353.67C1080,268.8,1011.2,200,926.33,200z M640,824c-100.52,0-182-81.48-182-182
s81.48-182,182-182s182,81.48,182,182S740.52,824,640,824z"
/>
</svg>

After

Width:  |  Height:  |  Size: 648 B

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1280 1280">
<path
fill="#FFFFFF"
d="M1219.85,1045.85l-224.8-83.69L854,850V645.44l317.06,43.14c33.1,4.5,63.58-18.68,68.08-51.77v0
c4.5-33.1-18.68-63.58-51.77-68.08l-434.95-59.19C792.87,491.12,821,450.35,821,403c0-64.62-52.38-117-117-117s-117,52.38-117,117
c0,51.94,33.85,95.97,80.69,111.25l-378.02-43.52c-2.67-0.31-5.31-0.42-7.92-0.38c4.76-10.41,4.8-22.84-0.97-33.74L80.93,59.64
c-9.87-18.61-32.95-25.7-51.56-15.83v0c-18.61,9.87-25.7,32.95-15.83,51.56l199.84,376.98c5.13,9.68,13.83,16.23,23.62,18.91
c-7.69,8.88-12.89,20.09-14.34,32.64v0c-3.82,33.18,19.98,63.18,53.17,67l323.91,37.29l0,0L598,810c0,0-128,2-189,5
c-53,3-75,65-46.05,106.24c0.05-0.24,136.94,189.62,136.94,189.62c19.6,26.61,57.05,32.29,83.66,12.69l0,0
c26.61-19.6,32.29-57.05,12.69-83.66L542,941l201-6l205.66,137.35l4.64,1.96l224.8,83.69c30.97,11.53,65.42-4.23,76.95-35.2v0
C1266.57,1091.83,1250.82,1057.38,1219.85,1045.85z"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1280 1280">
<path
fill="#FFFFFF"
d="M1014,1160c39,5,63-3,75.87-31.69l0,0c13.53-32.46-1.82-69.75-34.28-83.27L708.07,900.22l71.48-37.92
l321.62,145.05c32.72,14.76,71.2,0.2,85.96-32.52v0c7.38-16.36,8.52-35.88,3.74-53.15c-0.68-2.47-1.5-4.8-2.41-7.02l8.48-529.63
c0.33-20.64-16.13-37.64-36.77-37.97l0,0c-20.64-0.33-37.64,16.13-37.97,36.77l-7.66,478.4C974.56,800.83,832.1,745.34,692.3,683.52
c5.01,0.61,10.11,0.93,15.28,0.93c69.05,0,125.03-55.98,125.03-125.03S776.63,434.4,707.58,434.4s-125.03,55.98-125.03,125.03
c0,37.23,16.28,70.65,42.1,93.56c-142.4-65.53-282.26-137.13-424.6-202.8L409.6,121.57c11.1-17.41,5.99-40.51-11.42-51.61l0,0
c-17.41-11.1-40.51-5.99-51.61,11.42L126.42,426.63c-25.08,3.05-49.73,19.87-60.04,42.73l0,0c-14.76,32.72-0.2,71.2,32.52,85.96
l313.11,141.22l-68.3,175.18l-88.76,86.09c-20.92,20.29-25.68,50.93-14.09,75.96c3.82,8.45,9.46,16.24,16.9,22.74l115.03,100.42
c27.77,24.24,69.93,21.38,94.17-6.38h0c24.24-27.77,21.38-69.93-6.38-94.17l-60.8-53.08l19.39-18.81h130.07l0.02-0.01L1014,1160z"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

9
images/unknown.svg Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="48" height="48" version="1.1" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<path
d="m21.225 29.383c-0.09826-0.28968-0.22135-0.65379-0.3125-1.1086-0.09115-0.45481-0.13672-0.90394-0.13672-1.3474 0-0.69359 0.15625-1.319 0.46875-1.8761 0.32552-0.56852 0.72266-1.0972 1.1914-1.5862 0.48177-0.48892 0.99609-0.96079 1.543-1.4156 0.5599-0.45481 1.0742-0.89826 1.543-1.3303 0.48177-0.43207 0.87891-0.88688 1.1914-1.3644 0.32552-0.47755 0.48828-1.1434 0.48828-1.7944 0-0.58594-0.11719-1.0937-0.35156-1.5234-0.23438-0.44271-0.5599-0.80729-0.97656-1.0937-0.40364-0.29948-0.8724-0.52083-1.4062-0.66406-0.52083-0.14323-1.0807-0.21484-1.6797-0.21484-1.9401 0-3.7484 0.9081-5.5469 2.5977-0.56753 0.53317-1.5583-3.5623 0-4.5117 2.113-1.2875 4.349-1.875 6.6406-1.875 1.0547 0 2.0508 0.13672 2.9883 0.41016 0.9375 0.27344 1.7578 0.67708 2.4609 1.2109 0.70312 0.53386 1.2565 1.1979 1.6602 1.9922s0.60547 1.7187 0.60547 2.7734c0 1.0026-0.16927 1.9936-0.50781 2.6758-0.33854 0.68222-0.76823 1.3133-1.2891 1.8932-0.50781 0.56852-1.0612 1.0745-1.6602 1.5179-0.58594 0.44344-1.1393 0.88688-1.6602 1.3303-0.50781 0.44344-0.93099 0.90394-1.2695 1.3815-0.33854 0.47755-0.50781 1.0063-0.50781 1.5862 0 0.48892 0.07162 0.93236 0.21484 1.3303 0.14323 0.39796 0.29303 0.73083 0.42969 1.0063 0.31581 0.63652-3.8048 0.93258-4.1211-2e-6zm2.2266 8.3427c-0.74219 0-1.3997-0.25391-1.9727-0.76172-0.54688-0.49479-0.82031-1.1068-0.82031-1.8359 0-0.74219 0.27344-1.3542 0.82031-1.8359 0.5599-0.52083 1.2174-0.78125 1.9727-0.78125 0.74219 0 1.3932 0.26042 1.9531 0.78125 0.54688 0.48177 0.82031 1.0938 0.82031 1.8359 0 0.72917-0.27344 1.3411-0.82031 1.8359-0.57292 0.50781-1.224 0.76172-1.9531 0.76172z"
fill="#f9f9f9"
aria-label="?"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

31
index.html Normal file
View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>BS Overlay</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="cover"></div>
<div id="songData">
<div class="row">
<span id="title">Title</span>
<span id="subTitle">Subtitle</span>
</div>
<div class="row">
<span id="artist">Artist</span>
<span id="mapper">Mapper</span>
</div>
<div class="row">
<span id="difficulty">Easy</span>
<img id="characteristicIcon" src="images/characteristic/Standard.svg">
<span id="difficultyLabel">Diff Label</span>
<span id="bsrKey">25f</span>
<span id="type">WIP</span>
</div>
</div>
<script src="main.js"></script>
</body>
</html>

6
jsconfig.json Normal file
View File

@ -0,0 +1,6 @@
{
"compilerOptions": {
"checkJs": true,
"target": "es2021"
}
}

81
main.js Normal file
View File

@ -0,0 +1,81 @@
"use strict";
// https://github.com/hardcpp/BeatSaberPlus/wiki/%5BEN%5D-Song-Overlay
const bspUrl = "ws://localhost:2947/socket";
const retryMs = 5000;
let retries = 0;
function connect() {
console.log(`Connecting to ${bspUrl} (attempt ${retries++})`);
const ws = new WebSocket(bspUrl);
ws.onopen = () => console.log("Connection open.");
ws.onmessage = (e) => {
const data = JSON.parse(e.data);
switch (data._type) {
case "event":
switch (data._event) {
case "gameState":
document.body.className = data.gameStateChanged;
break;
case "mapInfo":
updateMapInfo(data.mapInfoChanged);
break;
}
break;
default:
console.log("message", e.data);
break;
}
};
ws.onclose = (e) => {
console.log(`Connection closed. code: ${e.code}, reason: ${e.reason}, clean: ${e.wasClean}`);
setTimeout(connect, retryMs);
};
}
const cover = document.getElementById("cover");
const title = document.getElementById("title");
const subTitle = document.getElementById("subTitle");
const artist = document.getElementById("artist");
const mapper = document.getElementById("mapper");
const difficulty = document.getElementById("difficulty");
const characteristicIcon = document.getElementById("characteristicIcon");
const difficultyLabel = document.getElementById("difficultyLabel");
const bsrKey = document.getElementById("bsrKey");
const type = document.getElementById("type");
/** @param {MapInfoChanged} data */
function updateMapInfo(data) {
const custom = data.level_id.startsWith("custom_level_");
cover.style.backgroundImage = data.coverRaw ? `url("data:image/jpeg;base64,${data.coverRaw}")` : "";
title.textContent = data.name || "";
subTitle.textContent = data.sub_name || "";
artist.textContent = data.artist || "";
mapper.textContent = data.mapper || "";
difficulty.textContent = data.difficulty.replace("Plus", " +") || "";
characteristicIcon.setAttribute("src", `images/characteristic/${data.characteristic}.svg`);
difficultyLabel.textContent = "";
bsrKey.textContent = data.BSRKey || "";
type.textContent = !custom ? "OST" : data.level_id.endsWith(" WIP") ? "WIP" : "";
if (custom) {
fetch(`https://api.beatsaver.com/maps/hash/${data.level_id.substring(13, 53)}`)
.then(response => response.json())
.then(map => {
if (!map.id) return;
bsrKey.textContent = map.id;
mapper.textContent = map.metadata.levelAuthorName; // Replace mapper name with full authors list
// Find difficulty label
const diff = map.versions[0].diffs.find(d =>
d.characteristic === data.characteristic && d.difficulty === data.difficulty
);
if (diff.label) difficultyLabel.textContent = diff.label;
});
}
}
connect();
document.documentElement.onclick = () => document.body.classList.toggle("Playing");

113
style.css Normal file
View File

@ -0,0 +1,113 @@
@font-face {
font-family: "Montserrat";
font-display: swap;
font-weight: 600 700;
src: url("fonts/montserrat.woff2") format("woff2");
}
:root {
font-size: 10px;
}
body {
display: flex;
margin: 0;
padding: 1.4rem 1.6rem;
font-family: "Montserrat", sans-serif;
white-space: nowrap;
background: #0000;
color: white;
filter: drop-shadow(0 0 0.1rem black) drop-shadow(0 0 0.1rem black);
transition: opacity 300ms ease-out;
transition-delay: 200ms;
}
body:not(.Playing) {
opacity: 0;
}
#cover {
width: 6.8rem;
height: 6.8rem;
border-radius: 0.6rem;
background-image: url("images/unknown.svg");
background-repeat: no-repeat;
background-size: contain;
flex-shrink: 0;
}
#songData {
display: flex;
flex-direction: column;
flex-grow: 1;
justify-content: space-between;
margin: -0.4rem 1.6rem -0.2rem;
overflow: hidden;
line-height: 1.2;
font-size: 1.5rem;
font-weight: 600;
}
#songData * {
overflow: hidden;
text-overflow: ellipsis;
}
#songData .row {
display: flex;
align-items: baseline;
column-gap: 0.6em;
}
#songData span:empty {
display: none;
}
#title {
font-size: 2.4rem;
font-weight: 700;
}
#subTitle,
#artist,
#mapper {
font-size: 1.6rem;
}
#mapper:before,
#difficultyLabel:before {
content: "";
}
#mapper:after,
#difficultyLabel:after {
content: "";
}
#difficultyLabel,
#subTitle {
flex-shrink: 99999;
}
#characteristicIcon {
width: 1.1em;
height: 1.1em;
flex-shrink: 0;
align-self: center;
}
#type,
#bsrKey {
font-size: 1.6rem;
font-weight: 700;
flex-shrink: 0;
}
#bsrKey {
letter-spacing: 0.2rem;
}
#bsrKey:before {
content: "!bsr ";
letter-spacing: normal;
}

20
types.d.ts vendored Normal file
View File

@ -0,0 +1,20 @@
interface Document {
getElementById(elementId: string): HTMLElement;
}
interface MapInfoChanged {
"level_id": string;
"name": string;
"sub_name": string;
"artist": string;
"mapper": string;
"characteristic": string;
"difficulty": string;
"duration": number;
"BPM": number;
"PP": number;
"BSRKey": string;
"coverRaw": string;
"time": number;
"timeMultiplier": number;
}