2048 update
This commit is contained in:
parent
b08d1b5ce7
commit
cd9955bfa8
237
2048.css
237
2048.css
@ -1274,3 +1274,240 @@ h1 {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
/* ==========================
|
||||
ADVANCED SOUND CONTROL
|
||||
========================== */
|
||||
.sound-control-container {
|
||||
position: fixed;
|
||||
top: clamp(10px, 2vh, 20px);
|
||||
left: clamp(10px, 2vw, 20px);
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
/* Main Sound Button */
|
||||
.btn-sound-main {
|
||||
width: clamp(40px, 6.5vw, 52px);
|
||||
height: clamp(40px, 6.5vw, 52px);
|
||||
padding: 0;
|
||||
background: rgba(30, 0, 50, 0.9);
|
||||
backdrop-filter: blur(15px);
|
||||
border: 2px solid rgba(0, 217, 255, 0.5);
|
||||
border-radius: 14px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow:
|
||||
0 5px 20px rgba(0, 0, 0, 0.4),
|
||||
0 0 25px rgba(0, 217, 255, 0.25),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.btn-sound-main svg {
|
||||
width: clamp(20px, 3.5vw, 26px);
|
||||
height: clamp(20px, 3.5vw, 26px);
|
||||
color: rgba(0, 234, 255, 0.95);
|
||||
transition: all 0.3s ease;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.btn-sound-main:hover {
|
||||
background: rgba(0, 234, 255, 0.18);
|
||||
border-color: rgba(0, 234, 255, 0.85);
|
||||
box-shadow:
|
||||
0 7px 28px rgba(0, 0, 0, 0.5),
|
||||
0 0 40px rgba(0, 234, 255, 0.4),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.15);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.btn-sound-main:hover svg {
|
||||
color: rgba(0, 234, 255, 1);
|
||||
transform: scale(1.12);
|
||||
}
|
||||
|
||||
.btn-sound-main:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* Muted State - Red */
|
||||
.btn-sound-main.all-muted {
|
||||
background: rgba(60, 0, 10, 0.9) !important;
|
||||
border-color: rgba(255, 60, 60, 0.7) !important;
|
||||
box-shadow:
|
||||
0 5px 20px rgba(0, 0, 0, 0.4),
|
||||
0 0 25px rgba(255, 60, 60, 0.3),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.1) !important;
|
||||
}
|
||||
|
||||
.btn-sound-main.all-muted svg {
|
||||
color: rgba(255, 80, 80, 0.95) !important;
|
||||
}
|
||||
|
||||
.btn-sound-main.all-muted:hover {
|
||||
background: rgba(255, 60, 60, 0.2) !important;
|
||||
border-color: rgba(255, 80, 80, 0.9) !important;
|
||||
}
|
||||
|
||||
/* Volume Panel */
|
||||
.volume-panel {
|
||||
position: absolute;
|
||||
top: clamp(46px, 8vh, 60px);
|
||||
left: 0;
|
||||
background: linear-gradient(145deg, rgba(20, 0, 40, 0.98), rgba(30, 0, 50, 0.98));
|
||||
backdrop-filter: blur(25px);
|
||||
border: 2px solid rgba(0, 217, 255, 0.4);
|
||||
border-radius: 18px;
|
||||
padding: 20px 18px;
|
||||
min-width: clamp(240px, 30vw, 280px);
|
||||
box-shadow:
|
||||
0 20px 60px rgba(0, 0, 0, 0.7),
|
||||
0 0 50px rgba(0, 217, 255, 0.3),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
animation: slideDown 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
|
||||
}
|
||||
|
||||
@keyframes slideDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-15px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Volume Item */
|
||||
.volume-item {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.volume-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.volume-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.volume-icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
color: rgba(0, 234, 255, 0.8);
|
||||
}
|
||||
|
||||
.volume-label {
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.volume-value {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: rgba(0, 234, 255, 0.9);
|
||||
min-width: 40px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* Volume Slider */
|
||||
.volume-slider {
|
||||
width: 100%;
|
||||
height: 6px;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 10px;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.volume-slider::-webkit-slider-track {
|
||||
height: 6px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.volume-slider::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background: linear-gradient(135deg, #00eaff 0%, #0099ff 100%);
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
box-shadow:
|
||||
0 2px 10px rgba(0, 234, 255, 0.5),
|
||||
0 0 20px rgba(0, 234, 255, 0.3);
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.volume-slider::-webkit-slider-thumb:hover {
|
||||
transform: scale(1.2);
|
||||
box-shadow:
|
||||
0 4px 15px rgba(0, 234, 255, 0.7),
|
||||
0 0 30px rgba(0, 234, 255, 0.5);
|
||||
}
|
||||
|
||||
.volume-slider::-moz-range-track {
|
||||
height: 6px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.volume-slider::-moz-range-thumb {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background: linear-gradient(135deg, #00eaff 0%, #0099ff 100%);
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
box-shadow:
|
||||
0 2px 10px rgba(0, 234, 255, 0.5),
|
||||
0 0 20px rgba(0, 234, 255, 0.3);
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.volume-slider::-moz-range-thumb:hover {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
/* Slider Fill Effect */
|
||||
.volume-slider {
|
||||
background: linear-gradient(to right,
|
||||
rgba(0, 234, 255, 0.3) 0%,
|
||||
rgba(0, 234, 255, 0.3) var(--value, 0%),
|
||||
rgba(255, 255, 255, 0.1) var(--value, 0%),
|
||||
rgba(255, 255, 255, 0.1) 100%);
|
||||
}
|
||||
|
||||
/* Mobile Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.volume-panel {
|
||||
min-width: clamp(220px, 50vw, 260px);
|
||||
padding: 16px 14px;
|
||||
}
|
||||
|
||||
.volume-item {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.volume-label {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.volume-value {
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
84
2048.html
84
2048.html
@ -16,42 +16,72 @@
|
||||
<div class="cursor-light" aria-hidden="true"></div>
|
||||
|
||||
<!-- SOUND CONTROLS - KIRI ATAS -->
|
||||
<div class="sound-controls">
|
||||
<!-- BG Music Toggle -->
|
||||
<button class="icon-btn btn-sound" id="btn-sound-bg" data-sound="bg" title="Toggle Background Music">
|
||||
<svg class="sound-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
||||
<path d="M9 18V5l12-2v13M9 18c0 1.657-1.343 3-3 3s-3-1.343-3-3 1.343-3 3-3 3 1.343 3 3zm12-2c0 1.657-1.343 3-3 3s-3-1.343-3-3 1.343-3 3-3 3 1.343 3 3z"/>
|
||||
<!-- SOUND CONTROL - KIRI ATAS -->
|
||||
<div class="sound-control-container">
|
||||
<!-- Main Sound Button -->
|
||||
<button class="icon-btn btn-sound-main" id="btn-sound-main" title="Sound Settings">
|
||||
<!-- Icon Full Sound (all ON) -->
|
||||
<svg class="sound-full" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
||||
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
|
||||
<path d="M15.54 8.46a5 5 0 0 1 0 7.07M19.07 4.93a10 10 0 0 1 0 14.14"/>
|
||||
</svg>
|
||||
<svg class="mute-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" style="display: none;">
|
||||
<line x1="2" y1="2" x2="22" y2="22"/>
|
||||
<path d="M9 18V5l12-2v13M9 18c0 1.657-1.343 3-3 3s-3-1.343-3-3 1.343-3 3-3 3 1.343 3 3zm12-2c0 1.657-1.343 3-3 3s-3-1.343-3-3 1.343-3 3-3 3 1.343 3 3z"/>
|
||||
<!-- Icon Medium Sound (some muted) -->
|
||||
<svg class="sound-medium" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" style="display: none;">
|
||||
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
|
||||
<path d="M15.54 8.46a5 5 0 0 1 0 7.07"/>
|
||||
</svg>
|
||||
<!-- Icon Low Sound (mostly muted) -->
|
||||
<svg class="sound-low" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" style="display: none;">
|
||||
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
|
||||
</svg>
|
||||
<!-- Icon Muted (all OFF) -->
|
||||
<svg class="sound-muted" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" style="display: none;">
|
||||
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
|
||||
<line x1="23" y1="9" x2="17" y2="15"/>
|
||||
<line x1="17" y1="9" x2="23" y2="15"/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Pop SFX Toggle -->
|
||||
<button class="icon-btn btn-sound" id="btn-sound-pop" data-sound="pop" title="Toggle Pop Sound">
|
||||
<svg class="sound-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<!-- Volume Sliders Panel (Hidden by default) -->
|
||||
<div class="volume-panel" id="volume-panel" style="display: none;">
|
||||
<!-- Music Volume -->
|
||||
<div class="volume-item">
|
||||
<div class="volume-header">
|
||||
<svg class="volume-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M9 18V5l12-2v13M9 18c0 1.657-1.343 3-3 3s-3-1.343-3-3 1.343-3 3-3 3 1.343 3 3zm12-2c0 1.657-1.343 3-3 3s-3-1.343-3-3 1.343-3 3-3 3 1.343 3 3z"/>
|
||||
</svg>
|
||||
<span class="volume-label">Music</span>
|
||||
<span class="volume-value" id="vol-music-display">25%</span>
|
||||
</div>
|
||||
<input type="range" class="volume-slider" id="vol-music" min="0" max="100" value="25">
|
||||
</div>
|
||||
|
||||
<!-- Pop SFX Volume -->
|
||||
<div class="volume-item">
|
||||
<div class="volume-header">
|
||||
<svg class="volume-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<path d="M8 12h8M12 8v8"/>
|
||||
</svg>
|
||||
<svg class="mute-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="display: none;">
|
||||
<line x1="2" y1="2" x2="22" y2="22"/>
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Merge SFX Toggle -->
|
||||
<button class="icon-btn btn-sound" id="btn-sound-merge" data-sound="merge" title="Toggle Merge Sound">
|
||||
<svg class="sound-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
||||
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
|
||||
</svg>
|
||||
<svg class="mute-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" style="display: none;">
|
||||
<line x1="2" y1="2" x2="22" y2="22"/>
|
||||
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<span class="volume-label">Pop SFX</span>
|
||||
<span class="volume-value" id="vol-pop-display">90%</span>
|
||||
</div>
|
||||
<input type="range" class="volume-slider" id="vol-pop" min="0" max="100" value="90">
|
||||
</div>
|
||||
|
||||
<!-- Merge SFX Volume -->
|
||||
<div class="volume-item">
|
||||
<div class="volume-header">
|
||||
<svg class="volume-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
|
||||
</svg>
|
||||
<span class="volume-label">Merge SFX</span>
|
||||
<span class="volume-value" id="vol-merge-display">100%</span>
|
||||
</div>
|
||||
<input type="range" class="volume-slider" id="vol-merge" min="0" max="100" value="100">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Top Right Controls -->
|
||||
<div class="top-controls">
|
||||
<button class="icon-btn btn-tutorial" id="btn-tutorial" title="How to Play">
|
||||
|
||||
165
2048.js
165
2048.js
@ -608,38 +608,165 @@ function hideGameOver() {
|
||||
/* =============================================
|
||||
SOUND CONTROLS
|
||||
============================================= */
|
||||
function toggleSound(soundType) {
|
||||
soundState[soundType] = !soundState[soundType];
|
||||
localStorage.setItem('sound_' + soundType, soundState[soundType]);
|
||||
updateAudioVolumes();
|
||||
/* =============================================
|
||||
ADVANCED VOLUME CONTROL SYSTEM
|
||||
============================================= */
|
||||
|
||||
const button = document.getElementById('btn-sound-' + soundType);
|
||||
if (button) {
|
||||
updateSoundButtonState(button, soundState[soundType]);
|
||||
// Volume State (0-100 for each sound)
|
||||
let volumeState = {
|
||||
music: parseInt(localStorage.getItem('vol_music')) || 25,
|
||||
pop: parseInt(localStorage.getItem('vol_pop')) || 90,
|
||||
merge: parseInt(localStorage.getItem('vol_merge')) || 100
|
||||
};
|
||||
|
||||
// Apply volumes on load
|
||||
function initVolumeControl() {
|
||||
// Set audio volumes
|
||||
audio.bg.volume = volumeState.music / 100;
|
||||
audio.pop.volume = volumeState.pop / 100;
|
||||
audio.merge.volume = volumeState.merge / 100;
|
||||
|
||||
// Update sliders
|
||||
const musicSlider = document.getElementById('vol-music');
|
||||
const popSlider = document.getElementById('vol-pop');
|
||||
const mergeSlider = document.getElementById('vol-merge');
|
||||
|
||||
if (musicSlider) {
|
||||
musicSlider.value = volumeState.music;
|
||||
updateSliderFill(musicSlider, volumeState.music);
|
||||
document.getElementById('vol-music-display').textContent = volumeState.music + '%';
|
||||
}
|
||||
|
||||
if (soundType === 'bg') {
|
||||
if (soundState.bg) {
|
||||
if (popSlider) {
|
||||
popSlider.value = volumeState.pop;
|
||||
updateSliderFill(popSlider, volumeState.pop);
|
||||
document.getElementById('vol-pop-display').textContent = volumeState.pop + '%';
|
||||
}
|
||||
|
||||
if (mergeSlider) {
|
||||
mergeSlider.value = volumeState.merge;
|
||||
updateSliderFill(mergeSlider, volumeState.merge);
|
||||
document.getElementById('vol-merge-display').textContent = volumeState.merge + '%';
|
||||
}
|
||||
|
||||
updateMainSoundIcon();
|
||||
|
||||
// Event listeners for sliders
|
||||
if (musicSlider) {
|
||||
musicSlider.addEventListener('input', (e) => {
|
||||
const val = parseInt(e.target.value);
|
||||
volumeState.music = val;
|
||||
audio.bg.volume = val / 100;
|
||||
localStorage.setItem('vol_music', val);
|
||||
document.getElementById('vol-music-display').textContent = val + '%';
|
||||
updateSliderFill(e.target, val);
|
||||
updateMainSoundIcon();
|
||||
|
||||
// Auto-play BG music if volume > 0
|
||||
if (val > 0 && audio.bg.paused) {
|
||||
tryPlayBg();
|
||||
} else {
|
||||
} else if (val === 0) {
|
||||
audio.bg.pause();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (popSlider) {
|
||||
popSlider.addEventListener('input', (e) => {
|
||||
const val = parseInt(e.target.value);
|
||||
volumeState.pop = val;
|
||||
audio.pop.volume = val / 100;
|
||||
localStorage.setItem('vol_pop', val);
|
||||
document.getElementById('vol-pop-display').textContent = val + '%';
|
||||
updateSliderFill(e.target, val);
|
||||
updateMainSoundIcon();
|
||||
});
|
||||
}
|
||||
|
||||
if (mergeSlider) {
|
||||
mergeSlider.addEventListener('input', (e) => {
|
||||
const val = parseInt(e.target.value);
|
||||
volumeState.merge = val;
|
||||
audio.merge.volume = val / 100;
|
||||
localStorage.setItem('vol_merge', val);
|
||||
document.getElementById('vol-merge-display').textContent = val + '%';
|
||||
updateSliderFill(e.target, val);
|
||||
updateMainSoundIcon();
|
||||
});
|
||||
}
|
||||
|
||||
// Toggle panel visibility
|
||||
const btnSoundMain = document.getElementById('btn-sound-main');
|
||||
const volumePanel = document.getElementById('volume-panel');
|
||||
|
||||
if (btnSoundMain && volumePanel) {
|
||||
btnSoundMain.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
const isVisible = volumePanel.style.display === 'block';
|
||||
volumePanel.style.display = isVisible ? 'none' : 'block';
|
||||
});
|
||||
|
||||
// Close panel when clicking outside
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!volumePanel.contains(e.target) && !btnSoundMain.contains(e.target)) {
|
||||
volumePanel.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// Prevent panel click from closing
|
||||
volumePanel.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function updateSoundButtonState(button, isEnabled) {
|
||||
if (!button) return;
|
||||
// Update slider fill effect
|
||||
function updateSliderFill(slider, value) {
|
||||
slider.style.setProperty('--value', value + '%');
|
||||
}
|
||||
|
||||
if (isEnabled) {
|
||||
button.classList.remove('muted');
|
||||
button.querySelector('.sound-icon').style.display = 'block';
|
||||
button.querySelector('.mute-icon').style.display = 'none';
|
||||
// Update main sound icon based on volumes
|
||||
function updateMainSoundIcon() {
|
||||
const btnMain = document.getElementById('btn-sound-main');
|
||||
if (!btnMain) return;
|
||||
|
||||
const iconFull = btnMain.querySelector('.sound-full');
|
||||
const iconMedium = btnMain.querySelector('.sound-medium');
|
||||
const iconLow = btnMain.querySelector('.sound-low');
|
||||
const iconMuted = btnMain.querySelector('.sound-muted');
|
||||
|
||||
// Calculate total volume average
|
||||
const totalVolume = volumeState.music + volumeState.pop + volumeState.merge;
|
||||
const avgVolume = totalVolume / 3;
|
||||
|
||||
// Hide all icons first
|
||||
iconFull.style.display = 'none';
|
||||
iconMedium.style.display = 'none';
|
||||
iconLow.style.display = 'none';
|
||||
iconMuted.style.display = 'none';
|
||||
|
||||
// Show appropriate icon based on average
|
||||
if (totalVolume === 0) {
|
||||
iconMuted.style.display = 'block';
|
||||
btnMain.classList.add('all-muted');
|
||||
} else {
|
||||
button.classList.add('muted');
|
||||
button.querySelector('.sound-icon').style.display = 'none';
|
||||
button.querySelector('.mute-icon').style.display = 'block';
|
||||
btnMain.classList.remove('all-muted');
|
||||
|
||||
if (avgVolume >= 60) {
|
||||
iconFull.style.display = 'block';
|
||||
} else if (avgVolume >= 30) {
|
||||
iconMedium.style.display = 'block';
|
||||
} else {
|
||||
iconLow.style.display = 'block';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize on DOM load (add this to your existing DOMContentLoaded)
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
// ... existing code ...
|
||||
initVolumeControl(); // ADD THIS LINE
|
||||
});
|
||||
|
||||
/* =============================================
|
||||
COMBO EFFECTS
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user