diff --git a/README.md b/README.md
index 5c12354..f824964 100644
--- a/README.md
+++ b/README.md
@@ -2,28 +2,29 @@
Simple Beat Saber stream overlay for [twitch.tv/iza_k](https://www.twitch.tv/iza_k)
-Requires [BeatSaberPlus](https://github.com/hardcpp/BeatSaberPlus) SongOverlay
+Requires [BeatSaberPlus](https://github.com/hardcpp/BeatSaberPlus)
### Preview
-
-## Usage - OBS Studio
-Add Source > Browser > URL: `https://bs-overlay.netlify.app/`
+
+
+## Usage
+
+1. Go to [bs-overlay.netlify.app](https://bs-overlay.netlify.app/)
+2. Click anywhere on page to show settings
+ 
+3. Copy URL
+4. OBS Studio: Add Source > Browser > paste URL
+5. BeatSaber+ settings, enable the Song Overlay module
+
+### Advanced
+
+[Download](https://github.com/ibillingsley/BeatSaber-Overlay/archive/refs/heads/main.zip) the source code to use the overlay locally without hosting it online.
+
+Note: in OBS browser source, use URL `file:///C:/path-to-overlay.../index.html` instead of "Local file" so that URL parameters work.
+
+You can further customize the overlay with Custom CSS. Example:
-### Configuration - Custom CSS
-To change size, set root font size (default 10px)
```css
-:root { font-size: 15px; }
-```
-To right align
-```css
-body > .row { flex-direction: row-reverse; } .row { justify-content: flex-end; }
-```
-To change fade duration (default 300ms)
-```css
-body { transition-duration: 500ms; }
-```
-To change font
-```css
-body { font-family: "Comic Sans MS", cursive; }
+body { font-family: "Comic Sans MS"; }
```
diff --git a/fonts/montserrat-600.woff2 b/fonts/montserrat-600.woff2
new file mode 100644
index 0000000..b8d0df8
Binary files /dev/null and b/fonts/montserrat-600.woff2 differ
diff --git a/fonts/montserrat-700.woff2 b/fonts/montserrat-700.woff2
new file mode 100644
index 0000000..b180294
Binary files /dev/null and b/fonts/montserrat-700.woff2 differ
diff --git a/fonts/montserrat.woff2 b/fonts/montserrat.woff2
deleted file mode 100644
index ab6fda2..0000000
Binary files a/fonts/montserrat.woff2 and /dev/null differ
diff --git a/images/screenshots/preview.png b/images/screenshots/preview.png
new file mode 100644
index 0000000..5ca240b
Binary files /dev/null and b/images/screenshots/preview.png differ
diff --git a/images/screenshots/settings.png b/images/screenshots/settings.png
new file mode 100644
index 0000000..8fb857c
Binary files /dev/null and b/images/screenshots/settings.png differ
diff --git a/index.css b/index.css
index eadc0c7..ec58b9b 100644
--- a/index.css
+++ b/index.css
@@ -1,27 +1,44 @@
@font-face {
font-family: "Montserrat";
font-display: swap;
- font-weight: 600 700;
- src: url("fonts/montserrat.woff2") format("woff2");
+ font-weight: 600;
+ src: url("fonts/montserrat-600.woff2") format("woff2");
+}
+
+@font-face {
+ font-family: "Montserrat";
+ font-display: swap;
+ font-weight: 700;
+ src: url("fonts/montserrat-700.woff2") format("woff2");
+}
+
+* {
+ box-sizing: border-box;
}
html {
- font-size: 10px;
+ font-size: calc(var(--scale, 1) * 10px);
}
body {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+ height: 100vh;
margin: 0;
padding: 1.4rem 1.6rem;
overflow: hidden;
font-family: "Montserrat", sans-serif;
- font-size: 1.5rem;
+ font-size: 1.6rem;
font-weight: 600;
line-height: 1.2;
white-space: nowrap;
background: rgba(0, 0, 0, 0);
color: white;
filter: drop-shadow(0 0 0.1rem black) drop-shadow(0 0 0.1rem black);
- transition: opacity 300ms ease-out;
+ transition-property: opacity;
+ transition-timing-function: ease-out;
+ transition-duration: calc(var(--fade, 300) * 1ms);
}
body.loading,
@@ -29,12 +46,27 @@ body:not([data-game-state="Playing"], .preview) {
opacity: 0;
}
+span:empty {
+ display: none;
+}
+
.row {
display: flex;
gap: 1.6rem;
+ align-items: baseline;
}
-.column {
+/* Map info */
+
+#coverImg {
+ width: 6.8rem;
+ height: 6.8rem;
+ border-radius: 0.6rem;
+ flex-shrink: 0;
+ align-self: flex-start;
+}
+
+#mapInfo {
display: flex;
flex-direction: column;
flex-grow: 1;
@@ -43,25 +75,13 @@ body:not([data-game-state="Playing"], .preview) {
overflow: hidden;
}
-.column * {
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-.column .row {
- align-items: baseline;
+#mapInfo .row {
gap: 0.9rem;
}
-.column span:empty {
- display: none;
-}
-
-#coverImg {
- width: 6.8rem;
- height: 6.8rem;
- border-radius: 0.6rem;
- flex-shrink: 0;
+#mapInfo * {
+ overflow: hidden;
+ text-overflow: ellipsis;
}
#title {
@@ -69,19 +89,13 @@ body:not([data-game-state="Playing"], .preview) {
font-weight: 700;
}
-#subTitle,
-#artist,
-#mapper {
- font-size: 1.6rem;
-}
-
-#mapper:before,
-#difficultyLabel:before {
+#mapper::before,
+#difficultyLabel::before {
content: "‹";
}
-#mapper:after,
-#difficultyLabel:after {
+#mapper::after,
+#difficultyLabel::after {
content: "›";
}
@@ -90,16 +104,20 @@ body:not([data-game-state="Playing"], .preview) {
flex-shrink: 99999;
}
+#difficulty,
+#difficultyLabel {
+ font-size: 1.5rem;
+}
+
#characteristicImg {
- width: 1.1em;
- height: 1.1em;
+ width: 1.7rem;
+ height: 1.7rem;
flex-shrink: 0;
align-self: center;
}
#type,
#bsrKey {
- font-size: 1.6rem;
font-weight: 700;
flex-shrink: 0;
}
@@ -108,7 +126,7 @@ body:not([data-game-state="Playing"], .preview) {
letter-spacing: 0.2rem;
}
-#bsrKey:before {
+#bsrKey::before {
content: "!bsr ";
letter-spacing: normal;
}
@@ -116,3 +134,100 @@ body:not([data-game-state="Playing"], .preview) {
#type:not(:empty) + #bsrKey {
display: none;
}
+
+/* Song time */
+
+#time {
+ align-self: flex-start;
+ width: 6.8rem;
+ font-size: 1.35rem;
+ text-align: right;
+ line-height: 1.4;
+ letter-spacing: -0.05em;
+}
+
+#time,
+#accuracy,
+#mistakes {
+ font-feature-settings: "tnum";
+ font-variant-numeric: tabular-nums;
+}
+
+/* Score */
+
+#score {
+ display: contents;
+}
+
+#accuracy {
+ font-size: 2.4rem;
+ font-weight: 700;
+}
+
+#accuracy::after {
+ display: inline-block;
+ content: "%";
+ font-size: 1.8rem;
+}
+
+#accuracy.failed {
+ text-decoration: line-through red;
+}
+
+#mistakes {
+ display: inline;
+ font-size: 1.8rem;
+}
+
+#mistakes::after {
+ content: "⤫";
+}
+
+#mistakes:empty::after {
+ content: "FC";
+}
+
+/* Settings */
+
+body:not(.cover) #coverImg,
+body:not(.mapInfo) #mapInfo,
+body:not(.time) #time,
+body:not(.score) #score {
+ display: none;
+}
+
+body.right > .row {
+ flex-direction: row-reverse;
+}
+
+body.right #mapInfo .row {
+ justify-content: flex-end;
+}
+
+body.bottom {
+ flex-direction: column-reverse;
+}
+
+body.bottom #time {
+ align-self: flex-end;
+}
+
+#settings {
+ display: none;
+ position: absolute;
+ top: 50%;
+ transform: translateY(-50%);
+ flex-direction: column;
+ gap: 2px;
+ font-family: system-ui;
+ font-size: 16px;
+}
+
+.preview #settings {
+ display: flex;
+}
+
+#settings label * {
+ float: right;
+ margin-left: 1em;
+}
diff --git a/index.html b/index.html
index 3f85796..17d1567 100644
--- a/index.html
+++ b/index.html
@@ -8,7 +8,7 @@