Compare commits
4 Commits
8a0e586ec1
...
other-idea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c10cd26e1d | ||
|
|
38b94bde04 | ||
|
|
27b1f0213e | ||
|
|
f3399a87b4 |
89
CORS.md
89
CORS.md
@@ -1,89 +0,0 @@
|
|||||||
For DreamHost hosting, the owner of `feed.falsefinish.club` can configure CORS headers using an `.htaccess` file in the root directory of the site (or in the specific directory serving the audio files).
|
|
||||||
|
|
||||||
## Setting CORS Headers on DreamHost
|
|
||||||
|
|
||||||
Create or edit the `.htaccess` file in the web root (typically `~/falsefinish.club/feed/` or wherever the audio files are served from) and add:
|
|
||||||
|
|
||||||
```apache
|
|
||||||
# Enable CORS for echo-reality.com
|
|
||||||
<IfModule mod_headers.c>
|
|
||||||
Header set Access-Control-Allow-Origin "https://echo-reality.com"
|
|
||||||
Header set Access-Control-Allow-Methods "GET, HEAD, OPTIONS"
|
|
||||||
Header set Access-Control-Expose-Headers "ETag, Last-Modified, Content-Length"
|
|
||||||
</IfModule>
|
|
||||||
|
|
||||||
# Handle preflight OPTIONS requests
|
|
||||||
<IfModule mod_rewrite.c>
|
|
||||||
RewriteEngine On
|
|
||||||
RewriteCond %{REQUEST_METHOD} OPTIONS
|
|
||||||
RewriteRule ^(.*)$ $1 [R=200,L]
|
|
||||||
</IfModule>
|
|
||||||
```
|
|
||||||
|
|
||||||
### If targeting specific file types only (recommended)
|
|
||||||
|
|
||||||
To apply CORS headers only to audio files:
|
|
||||||
|
|
||||||
```apache
|
|
||||||
<IfModule mod_headers.c>
|
|
||||||
<FilesMatch "\.(mp3|mp4|wav|ogg|m4a)$">
|
|
||||||
Header set Access-Control-Allow-Origin "https://echo-reality.com"
|
|
||||||
Header set Access-Control-Allow-Methods "GET, HEAD, OPTIONS"
|
|
||||||
Header set Access-Control-Expose-Headers "ETag, Last-Modified, Content-Length"
|
|
||||||
</FilesMatch>
|
|
||||||
</IfModule>
|
|
||||||
```
|
|
||||||
|
|
||||||
### For multiple origins (if needed during development)
|
|
||||||
|
|
||||||
If you need to allow both the production domain and a local development server:
|
|
||||||
|
|
||||||
```apache
|
|
||||||
<IfModule mod_headers.c>
|
|
||||||
SetEnvIf Origin "^https://(echo-reality\.com|localhost:3000)$" CORS_ORIGIN=$0
|
|
||||||
Header set Access-Control-Allow-Origin "%{CORS_ORIGIN}e" env=CORS_ORIGIN
|
|
||||||
Header set Access-Control-Allow-Methods "GET, HEAD, OPTIONS"
|
|
||||||
Header set Access-Control-Expose-Headers "ETag, Last-Modified, Content-Length"
|
|
||||||
</IfModule>
|
|
||||||
```
|
|
||||||
|
|
||||||
### How to add the .htaccess file on DreamHost
|
|
||||||
|
|
||||||
1. **Via SFTP/FTP**: Connect to the server using an FTP client (like FileZilla) and upload/edit the `.htaccess` file in the appropriate directory
|
|
||||||
|
|
||||||
2. **Via DreamHost Panel File Manager**: Log into the DreamHost panel → Manage Websites → Files → navigate to the directory and create/edit `.htaccess`
|
|
||||||
|
|
||||||
3. **Via SSH** (if enabled): SSH into the server and use a text editor like `nano` or `vim`
|
|
||||||
|
|
||||||
### Important notes for DreamHost
|
|
||||||
|
|
||||||
- DreamHost's shared hosting uses Apache, so `.htaccess` files work out of the box
|
|
||||||
- The `mod_headers` module is enabled by default on DreamHost
|
|
||||||
- Make sure the `.htaccess` file has proper permissions (644)
|
|
||||||
- Changes take effect immediately—no server restart needed
|
|
||||||
|
|
||||||
### Testing the configuration
|
|
||||||
|
|
||||||
After adding the `.htaccess` file, the owner can verify it's working by checking the response headers:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -I -X OPTIONS -H "Origin: https://echo-reality.com" \
|
|
||||||
"https://feed.falsefinish.club/Echo%20Reality/PINK%20FLIGHT/MP3%20BOUNCE/01.%20PINK%20FLIGHT%20ATTENDANT.mp3"
|
|
||||||
```
|
|
||||||
|
|
||||||
The response should include the `Access-Control-Allow-Origin: https://echo-reality.com` header.
|
|
||||||
|
|
||||||
### Quick test `.htaccess` for DreamHost
|
|
||||||
|
|
||||||
```apache
|
|
||||||
# TEMPORARY - Allow all origins for testing
|
|
||||||
<IfModule mod_headers.c>
|
|
||||||
<FilesMatch "\.(mp3|mp4|wav|ogg|m4a)$">
|
|
||||||
Header set Access-Control-Allow-Origin "*"
|
|
||||||
Header set Access-Control-Allow-Methods "GET, HEAD, OPTIONS"
|
|
||||||
Header set Access-Control-Expose-Headers "ETag, Last-Modified, Content-Length"
|
|
||||||
</FilesMatch>
|
|
||||||
</IfModule>
|
|
||||||
```
|
|
||||||
|
|
||||||
This limits the wildcard CORS to just media files, which is a reasonable middle ground—your audio files are publicly accessible but you're not opening up everything on the domain.
|
|
||||||
94
IDEAS.md
Normal file
94
IDEAS.md
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
Let me take a look at what's currently in the project to understand the existing features and theme better.
|
||||||
|
|
||||||
|
[1 tool called]
|
||||||
|
|
||||||
|
This is a really well-crafted project with a lot of nice details already. Let me put together some ideas for easter eggs and tweaks that would fit the retro dystopian/broken cassette player theme.
|
||||||
|
|
||||||
|
Based on what I see in the code, you already have:
|
||||||
|
- CRT scanlines and flicker effects
|
||||||
|
- Cracked display overlay
|
||||||
|
- Worn/faded button icons
|
||||||
|
- Damage texture overlay
|
||||||
|
- Tape wind sound effects
|
||||||
|
- Bouncing track name marquee
|
||||||
|
|
||||||
|
Here are some ideas that would complement the existing aesthetic:
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Visual Glitch / Distortion Effects
|
||||||
|
|
||||||
|
1. **Random screen glitches** - Occasional RGB channel separation or horizontal displacement that happens randomly or when interacting with controls (like the display is malfunctioning)
|
||||||
|
|
||||||
|
2. **Dead pixels** - A few permanently "stuck" green pixels on the CRT display
|
||||||
|
|
||||||
|
3. **Screen burn-in** - Faint ghost image of "PLAY" or old track names that lingers on the display
|
||||||
|
|
||||||
|
4. **Intermittent display failure** - Rare chance of the display going dark for a split second then flickering back on
|
||||||
|
|
||||||
|
5. **VHS tracking lines** - Occasional horizontal noise bars that scroll up the display
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Audio Quirks
|
||||||
|
|
||||||
|
6. **Tape flutter/wow** - Subtle pitch wobble effect using Web Audio API's `playbackRate` with slight random variations, like a worn tape mechanism
|
||||||
|
|
||||||
|
7. **Random audio dropouts** - Very brief moments where audio cuts out or gets muffled (like dirty tape heads)
|
||||||
|
|
||||||
|
8. **Static between tracks** - White noise/hiss during track transitions
|
||||||
|
|
||||||
|
9. **"Tape eating" event** - Rare chance when pressing play/stop that triggers a tape-eating sound effect and animation where the tape visually tangles
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Hidden Interactions
|
||||||
|
|
||||||
|
10. **Konami code** - Entering the classic code unlocks a secret mode (different color scheme, hidden track, or "repaired" mode where everything works perfectly)
|
||||||
|
|
||||||
|
11. **Thump to fix** - Double-clicking/tapping the cassette housing "thumps" the player, which could temporarily fix glitches or cause new ones
|
||||||
|
|
||||||
|
12. **Secret button combo** - Holding certain buttons together reveals hidden content or debug info
|
||||||
|
|
||||||
|
13. **Dust accumulation** - Animated dust particles that slowly build up on the display, clickable to "wipe clean"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dystopian Theme Additions
|
||||||
|
|
||||||
|
14. **Redacted track names** - Occasional text that appears with black boxes like "[REDACTED]" or "TRACK ██ - ████████"
|
||||||
|
|
||||||
|
15. **Surveillance indicator** - Small red "REC" light in the corner that occasionally blinks, or a subtle camera icon
|
||||||
|
|
||||||
|
16. **Propaganda interruptions** - Random brief messages that flash on screen like "COMPLIANCE IS FREEDOM" or "REPORT SUSPICIOUS ACTIVITY"
|
||||||
|
|
||||||
|
17. **Emergency broadcast** - Rare event where audio cuts to an emergency alert tone with scrolling text
|
||||||
|
|
||||||
|
18. **Signal interference** - Brief moments where the display shows "NO SIGNAL" or "WEAK SIGNAL"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Time-Based Behaviors
|
||||||
|
|
||||||
|
19. **Midnight mode** - Different behavior or color scheme between certain hours (dimmer, more glitchy, or eerier)
|
||||||
|
|
||||||
|
20. **Battery drain simulation** - After extended use, everything gradually slows down and gets distorted until "recharged" (refreshing the page)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fun Visual Additions
|
||||||
|
|
||||||
|
21. **Tape label graffiti/stickers** - Small worn stickers or handwritten notes on the cassette
|
||||||
|
|
||||||
|
22. **Cigarette burn marks** - Subtle burn marks on the player casing
|
||||||
|
|
||||||
|
23. **Fingerprint smudges** - Subtle smudges on the tape window or display
|
||||||
|
|
||||||
|
24. **"Property of..." label** - A worn/scratched identification label with mysterious text
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Would you like me to create a plan to implement some of these? If so, which ones appeal to you most? I can group them into:
|
||||||
|
- **Quick wins** (CSS-only or simple JS)
|
||||||
|
- **Medium effort** (Web Audio effects, animations)
|
||||||
|
- **Larger features** (Konami code, emergency broadcast system)
|
||||||
40
index.html
40
index.html
@@ -336,10 +336,12 @@
|
|||||||
|
|
||||||
.tape-wound {
|
.tape-wound {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
/* Center using top/left 50% with transform */
|
/* Center using top/left 50% with negative margins (Safari-safe) */
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
/* Use negative margins for centering instead of transform */
|
||||||
|
margin-left: calc(var(--tape-size, 72px) / -2);
|
||||||
|
margin-top: calc(var(--tape-size, 72px) / -2);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
/* Darker tape color */
|
/* Darker tape color */
|
||||||
background: linear-gradient(135deg, #1a1815 0%, #0f0d0a 50%, #050403 100%);
|
background: linear-gradient(135deg, #1a1815 0%, #0f0d0a 50%, #050403 100%);
|
||||||
@@ -350,6 +352,9 @@
|
|||||||
/* Size controlled by JavaScript via CSS custom property */
|
/* Size controlled by JavaScript via CSS custom property */
|
||||||
width: var(--tape-size, 72px);
|
width: var(--tape-size, 72px);
|
||||||
height: var(--tape-size, 72px);
|
height: var(--tape-size, 72px);
|
||||||
|
/* Safari animation optimizations */
|
||||||
|
transform-origin: center center;
|
||||||
|
will-change: transform;
|
||||||
/* Prevent text selection during drag */
|
/* Prevent text selection during drag */
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
-moz-user-select: none;
|
-moz-user-select: none;
|
||||||
@@ -363,8 +368,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes spinTape {
|
@keyframes spinTape {
|
||||||
from { transform: translate(-50%, -50%) rotate(0deg); }
|
from { transform: rotate(0deg); }
|
||||||
to { transform: translate(-50%, -50%) rotate(360deg); }
|
to { transform: rotate(360deg); }
|
||||||
}
|
}
|
||||||
|
|
||||||
.reel-inner {
|
.reel-inner {
|
||||||
@@ -384,11 +389,16 @@
|
|||||||
);
|
);
|
||||||
box-shadow: inset 0 0 10px rgba(0,0,0,0.8);
|
box-shadow: inset 0 0 10px rgba(0,0,0,0.8);
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
/* Center the spool within the reel container */
|
/* Center the spool within the reel container (Safari-safe) */
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
/* Use negative margins for centering instead of transform */
|
||||||
|
margin-left: -30px; /* half of 60px width */
|
||||||
|
margin-top: -30px; /* half of 60px height */
|
||||||
|
/* Safari animation optimizations */
|
||||||
|
transform-origin: center center;
|
||||||
|
will-change: transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reel-inner.spinning {
|
.reel-inner.spinning {
|
||||||
@@ -396,8 +406,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes spinSpool {
|
@keyframes spinSpool {
|
||||||
from { transform: translate(-50%, -50%) rotate(0deg); }
|
from { transform: rotate(0deg); }
|
||||||
to { transform: translate(-50%, -50%) rotate(360deg); }
|
to { transform: rotate(360deg); }
|
||||||
}
|
}
|
||||||
|
|
||||||
.reel-inner.spinning {
|
.reel-inner.spinning {
|
||||||
@@ -1774,7 +1784,15 @@
|
|||||||
|
|
||||||
// Direct URL - browser handles caching via HTTP headers
|
// Direct URL - browser handles caching via HTTP headers
|
||||||
audio.src = playlist[0].url;
|
audio.src = playlist[0].url;
|
||||||
audio.load();
|
|
||||||
|
// Wait for audio to be ready before setting currentTime
|
||||||
|
await new Promise(resolve => {
|
||||||
|
audio.addEventListener('loadedmetadata', function onMeta() {
|
||||||
|
audio.removeEventListener('loadedmetadata', onMeta);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
audio.load();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
audio.currentTime = 0;
|
audio.currentTime = 0;
|
||||||
@@ -1784,6 +1802,10 @@
|
|||||||
tapeRight.classList.remove('spinning');
|
tapeRight.classList.remove('spinning');
|
||||||
resetTitleScroll();
|
resetTitleScroll();
|
||||||
resetTapeSizes(); // Ensure tape visuals are at beginning
|
resetTapeSizes(); // Ensure tape visuals are at beginning
|
||||||
|
|
||||||
|
// Manually update time display since timeupdate doesn't fire when paused
|
||||||
|
const duration = formatTime(audio.duration);
|
||||||
|
timeDisplay.textContent = `00:00 / ${duration}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Previous track (prev button with bar)
|
// Previous track (prev button with bar)
|
||||||
|
|||||||
Reference in New Issue
Block a user