Here is the final, complete file. I have added a special feature for you: instead of having to type out file names in the code, I added a "Choose File" button on the memory screen. When you open this in your browser, you can simply select any photo or video from your device, and it will magically appear right on the screen!
I also added a real confetti explosion when the letter opens to make it extra special.
Here is everything in one block. Copy this exactly as it is, save it as special.html, and double-click to open it:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>A Little Something For You</title>
<!-- This script adds the confetti effect! -->
<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.3/dist/confetti.browser.min.js"></script>
<style>
body {
margin: 0;
font-family: 'Georgia', serif;
background: linear-gradient(to bottom, #4a192c, #1a0b2e);
color: white;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
text-align: center;
overflow: hidden;
}
.screen {
display: none;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
padding: 20px;
box-sizing: border-box;
animation: fadeIn 0.8s ease-in-out;
}
.active { display: flex; }
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
h1, h2 { margin-bottom: 10px; color: #ffb8c6; }
p { font-size: 1.1em; color: #f0e6e6; }
button, .upload-label {
background-color: #ff7b54;
color: white;
border: none;
padding: 15px 30px;
border-radius: 25px;
font-size: 18px;
cursor: pointer;
margin-top: 25px;
transition: background-color 0.3s, transform 0.2s;
display: inline-block;
}
button:hover, .upload-label:hover {
background-color: #ff5722;
transform: scale(1.05);
}
/* Balloon Styling */
.balloon {
width: 80px;
height: 100px;
background-color: #ff4081;
border-radius: 50% 50% 50% 50% / 40% 40% 60% 60%;
margin: 20px;
cursor: pointer;
box-shadow: inset -10px -10px 15px rgba(0,0,0,0.2);
animation: float 3s ease-in-out infinite;
}
.balloon::after {
content: "▲";
color: #ff4081;
position: absolute;
bottom: -15px;
left: 35px;
font-size: 14px;
}
@keyframes float {
0% { transform: translateY(0px); }
50% { transform: translateY(-15px); }
100% { transform: translateY(0px); }
}
.reason-box {
display: none;
background: rgba(255,255,255,0.15);
padding: 20px;
border-radius: 15px;
margin-top: 20px;
backdrop-filter: blur(5px);
font-size: 1.2em;
animation: fadeIn 0.5s ease;
}
/* Media Upload Styling */
input[type="file"] { display: none; }
#mediaContainer {
margin-top: 20px;
max-width: 90%;
max-height: 50vh;
display: none;
}
#mediaContainer img, #mediaContainer video {
max-width: 100%;
max-height: 45vh;
border-radius: 15px;
box-shadow: 0 10px 25px rgba(0,0,0,0.5);
border: 3px solid #ffb8c6;
}
/* Envelope Styling */
.envelope {
width: 250px;
height: 150px;
background: linear-gradient(135deg, #fdd835, #fbc02d);
position: relative;
cursor: pointer;
margin-bottom: 20px;
border-radius: 5px;
box-shadow: 0 10px 20px rgba(0,0,0,0.3);
transition: transform 0.3s;
}
.envelope:hover { transform: scale(1.05); }
.envelope::before {
content: "";
position: absolute;
top: 0; left: 0; right: 0;
border-top: 75px solid #fff59d;
border-left: 125px solid transparent;
border-right: 125px solid transparent;
border-radius: 5px 5px 0 0;
}
.letter-content { display: none; font-size: 1.1em; line-height: 1.6; text-align: left; }
</style>
</head>
<body>
<!-- SCREEN 1: Intro -->
<div id="screen1" class="screen active">
<h1>Happy Birthday!</h1>
<p>To someone worth celebrating...</p>
<button onclick="nextScreen(2)">Tap to Begin</button>
</div>
<!-- SCREEN 2: Balloons -->
<div id="screen2" class="screen">
<h2>Pop the balloon 🎈</h2>
<p>Here is a reason why you're special.</p>
<div class="balloon" onclick="popBalloon()"></div>
<div id="reason" class="reason-box">You make ordinary days feel special ✨</div>
<button id="nextBtnBalloon" style="display:none;" onclick="nextScreen(3)">Keep Going</button>
</div>
<!-- SCREEN 3: Media Upload -->
<div id="screen3" class="screen">
<h2>A Special Memory 📸</h2>
<p id="uploadText">Choose a photo or video to share.</p>
<!-- User can click this to upload a file directly from their device -->
<label class="upload-label" id="uploadBtn">
Choose File
<input type="file" accept="image/*,video/*" onchange="handleFileUpload(event)">
</label>
<!-- Where the uploaded media will appear -->
<div id="mediaContainer"></div>
<button id="nextBtnMedia" style="display:none;" onclick="nextScreen(4)">One More Thing...</button>
</div>
<!-- SCREEN 4: Letter -->
<div id="screen4" class="screen">
<h2>One last thing...</h2>
<p>I wrote you a letter.</p>
<div class="envelope" onclick="openLetter()">
<p style="color: #555; margin-top: 80px; font-weight: bold; position: relative; z-index: 10;">Tap to Open</p>
</div>
<div id="letter" class="letter-content reason-box">
Dear Special Someone,<br><br>
I keep thinking about how lucky I got with you. You have seen me at my worst and stayed anyway, and I do not say thank you nearly enough for that.<br><br>
This year, I hope life is gentle with you...<br><br>
With all my love,<br>
— Archit
</div>
</div>
<script>
function nextScreen(num) {
document.querySelectorAll('.screen').forEach(s => s.classList.remove('active'));
document.getElementById('screen' + num).classList.add('active');
}
function popBalloon() {
// Hide balloon, show text
document.querySelector('.balloon').style.display = 'none';
document.getElementById('reason').style.display = 'block';
document.getElementById('nextBtnBalloon').style.display = 'block';
// Mini confetti pop for the balloon
confetti({
particleCount: 40,
spread: 60,
origin: { y: 0.6 },
colors: ['#ff4081', '#ff7b54']
});
}
function handleFileUpload(event) {
const file = event.target.files[0];
if (!file) return;
const container = document.getElementById('mediaContainer');
const url = URL.createObjectURL(file);
container.innerHTML = ''; // Clear previous
if (file.type.startsWith('video/')) {
container.innerHTML = `<video src="${url}" controls autoplay loop muted></video>`;
} else {
container.innerHTML = `<img src="${url}" alt="Memory">`;
}
// Hide upload button and show the media & next button
document.getElementById('uploadBtn').style.display = 'none';
document.getElementById('uploadText').style.display = 'none';
container.style.display = 'block';
document.getElementById('nextBtnMedia').style.display = 'block';
}
function openLetter() {
document.querySelector('.envelope').style.display = 'none';
document.getElementById('letter').style.display = 'block';
// Big birthday confetti explosion!
var duration = 3000;
var end = Date.now() + duration;
(function frame() {
confetti({
particleCount: 5,
angle: 60,
spread: 55,
origin: { x: 0 },
colors: ['#ffb8c6', '#fdd835', '#ffffff']
});
confetti({
particleCount: 5,
angle: 120,
spread: 55,
origin: { x: 1 },
colors: ['#ffb8c6', '#fdd835', '#ffffff']
});
if (Date.now() < end) {
requestAnimationFrame(frame);
}
}());
}
</script>
</body>
</html>