By Kantaraj Luitel (@susantedit) • 10 min read • 🔗 Live Web App
"This isn't a simple Raksha Bandhan text wish. It's a story-driven, interactive 3D WebGL Rakhi & Sibling Tax negotiation experience built with Three.js and zero-server Web Crypto cryptography."
![]()
Introduction: Why a Text Message Wasn't Enough
When Raksha Bandhan rolls around, typing "Happy Raksha Bandhan Sujita! 🧵✨" into WhatsApp feels like an absolute cop-out.
When you've grown up together sharing snacks, fighting over the TV remote, borrowing chargers without permission, and pulling off endless pranks, a generic text or a plain e-card simply doesn't convey the bond. Sibling relationships are built on an affectionate mix of unconditional love and playful extortion—what we lovingly call the Sibling Tax.
I wanted to give my sister Sujita (and siblings everywhere) an experience they'd never forget: anithor bond (anithor.site) — Rakhi with Digital Love.
Imagine opening a single link on your smartphone browser. Suddenly, you're presented with a glowing 3D Rakhi floating on your screen. You select braided zari threads, place traditional mithai like Kaju Barfi, Motichoor Ladoo, and Rasbari onto a brass pooja thali, and attach an itemized invoice for years of unpaid sisterly labor.
When your brother opens the link, he doesn't just see a bill—he has to physically drag the diya in a circle to complete the Aarti, long-press to tie the Rakhi, and then fight back! He can lock his gift inside a 3D metallic vault that you have to tap open, force you into a signed budget contract, or let a fair-odds Shagun Roulette wheel decide his financial fate.
Building this wasn't just a festive gift; it was an engineering exercise in Three.js WebGL graphics, client-side Web Crypto AES-GCM encryption, URL hash state compression, and single-channel audio synchronization.
Here is the complete story of how I built anithor bond, translated the sibling banter into 3D interactions, and solved the technical hurdles of running zero-server encrypted web applications on mobile browsers.
The Culture of Sibling Banter: Translating Negotiations into 3D
In Nepalese and South Asian culture, Raksha Bandhan is a celebration of protection and love, but it's also traditionally accompanied by shagun (gifts or cash given by the brother).
When building anithor bond, I wanted every step of the web application to radiate warmth, humor, and cultural authenticity. Here are some of the pre-filled invoice lines and defense clauses directly from the codebase:
Invoice Item 1: The Remote Control Tax
- Original Item: "Emotional damage from remote control fights"
- Default Amount:
Rs. 501 - Why it's in the app: Every sibling knows the battle for the TV remote during childhood. Itemizing it on a formal bill makes the nostalgia instantly hilarious.
Invoice Item 2: Secret Keeping Fee
- Original Item: "Covert operations & secret keeping fee"
- Default Amount:
Rs. 1,001 - Why it's in the app: Sisters are the ultimate vault of secrets when parents start asking questions. Charging for secret retention is pure sibling business.
Brother Defense Clause: The Charger Non-Interference Act
- Original Contract Clause: "No touching my phone charger ever again"
- Cap Amount:
Rs. 101 - Why it's in the app: When the brother counters with a Budget Contract, he enforces non-negotiable household rules before releasing the shagun.
The Engineering Challenge
While the concept was joyful, the technical requirements were strictly demanding:
- Zero-Server Privacy & URL Length Limits: No database, no user registration, and no cloud storage. Every piece of state—3D Rakhi specifications, thali items, invoice line items, encrypted notes, and Base64 QR payment images—had to fit entirely inside the URL hash fragment (
#s=,#b=,#sr=). Standard URL limits in messaging apps like WhatsApp cap at ~3,000 characters. - Mobile WebGL Render Performance: Rendering 3D braided thread meshes, metallic thalis, reflections, and dynamic lighting on budget smartphones requires careful memory management, geometry pooling, and shadow map scaling.
- Single-Channel Audio Conflict Resolution: Web browsers restrict audio playback. The app features auto-playing background music (
/videoplayback.m4a) and meme sound effects (/paisa hi paisa hoga.m4a,/nothing.m4a,/brother.m4a,/bless.m4a). If background music continues playing over meme audio, it creates unpleasant double-music overlap. - High-Res Canvas Exporter: Generating 1080×1920 Instagram Story Cards client-side with full typography, 2D emblem artwork, and logo watermarks without relying on heavy server-side image renderers like Puppeteer.

What I Built
anithor bond is built around two complementary user flows:
- Flow 1 (Sister Initiates): Sister builds a 3D Rakhi, sets up the thali, itemizes the invoice, attaches her Nepal payment QR code, and generates an encrypted
#s=URL. Brother opens it, performs Aarti, ties the Rakhi, and chooses his response (#br=). - Flow 2 (Brother Pre-emptive Defense): Brother strikes first by building a Shagun Vault, Budget Contract, or Roulette wheel, generating an encrypted
#b=URL. Sister opens it, ties the Rakhi to break the seal, cracks the vault, and returns her verdict (#sr=).
+-------------------------------------------------------------------+
| SISTER PHONE (BROWSER) |
| |
| +--------------------+ Web Crypto AES +-------------------+ |
| | 3D Rakhi & Invoice | -----------------> | URL Hash Capsule | |
| | (anithor.site) | | (#s=...) | |
| +--------------------+ +-------------------+ |
+------------------------------------------------------+------------+
| Share via WhatsApp
v
+-------------------------------------------------------------------+
| BROTHER PHONE (BROWSER) |
| |
| +--------------------+ Unpack & Tie +-------------------+ |
| | Aarti & 3D Rakhi | <----------------- | Client Decrypt | |
| | Settlement View | | (#s=...) | |
| +--------------------+ +-------------------+ |
+-------------------------------------------------------------------+
Tech Stack
| Layer | Technology | Engineering Purpose |
|---|---|---|
| Frontend UI | React 18, TypeScript, Vite | Component architecture, state management, screen routing |
| 3D Rendering | Three.js, @react-three/fiber, @react-three/drei |
3D Rakhi mesh generation, Thali lighting, Vault physics & animations |
| Cryptography | Web Crypto API (AES-GCM 256-bit) |
In-browser zero-server encryption and payload sealing |
| Compression | CompressionStream (deflate-raw) |
Payload binary compression to fit URL length constraints |
| Audio Engine | HTML5 Audio Manager | Background music looping, volume fading, single-channel meme sound interruption |
| Canvas Graphics | HTML5 2D Context | Client-side 1080×1920 Instagram Story Card PNG rendering |
Key Features
1. Client-Side AES-GCM URL Compression Architecture
To guarantee 100% privacy without a database backend, payloads pass through CompressionStream('deflate-raw') followed by Web Crypto API AES-GCM encryption. The initial 12 bytes serve as the initialization vector (IV), and the resulting byte array is Base64-URL encoded into the hash.
2. Physical Tap-Cracking 3D Troll Vault
Brothers can lock shagun inside a safe requiring up to 120 rapid taps (useTapCounter). As the sister taps, the vault pulses (tapPulse), cracks open on completion, and triggers confetti particle physics (<Burst />).

3. Single-Channel Audio Synchronizer
To eliminate double-music overlap, an active audio coordinator was implemented. When a meme sound effect starts, background music is explicitly paused. Once the meme sound finishes (effect.onended), background music resumes seamlessly.
4. Compact QR Image Compressor
Uploaded Nepal QR payment images are scaled down to 220px JPEG @ 0.5 quality using HTML5 canvas, compressing Base64 data URLs to ~2.5kB to ensure capsule links stay safely under 3,000 characters.
Important Technical Implementation
1. Client-Side Cryptography & Capsule Encoding (src/lib/crypto.ts)
The core security of anithor bond lies in browser-native AES-GCM key derivation and raw stream compression:
const FIXED_KEY_BYTES = new Uint8Array([
0x61, 0x6e, 0x69, 0x74, 0x68, 0x6f, 0x72, 0x2d,
0x62, 0x6f, 0x6e, 0x64, 0x2d, 0x32, 0x30, 0x32,
0x36, 0x2d, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65,
0x2d, 0x6b, 0x65, 0x79, 0x2d, 0x76, 0x31, 0x21,
]);
async function getKey(): Promise<CryptoKey> {
return crypto.subcrypto.importKey(
'raw',
FIXED_KEY_BYTES,
{ name: 'AES-GCM' },
false,
['encrypt', 'decrypt']
);
}
export async function encryptPayload<T>(data: T): Promise<string> {
const json = JSON.stringify(data);
const compressed = await compressString(json);
const key = await getKey();
const iv = crypto.getRandomValues(new Uint8Array(12));
const ciphertext = await crypto.subcrypto.encrypt(
{ name: 'AES-GCM', iv },
key,
compressed
);
const combined = new Uint8Array(iv.length + ciphertext.byteLength);
combined.set(iv, 0);
combined.set(new Uint8Array(ciphertext), iv.length);
return toBase64Url(combined);
}
2. Single-Channel Audio Controller (src/lib/audio.ts)
To ensure background audio (/videoplayback.m4a) and meme songs (/paisa hi paisa hoga.m4a, /nothing.m4a, /bless.m4a) never overlap or clash when unmuting:
let bgAudio: HTMLAudioElement | null = null;
let currentEffectAudio: HTMLAudioElement | null = null;
let muted = false;
export function toggleMute(): boolean {
muted = !muted;
const bg = getBgAudio();
bg.muted = muted;
if (currentEffectAudio) {
currentEffectAudio.muted = muted;
}
// Only resume background music if NO meme effect is currently playing
if (!muted && bg.paused && !currentEffectAudio) {
bg.play().catch(() => {});
}
return muted;
}
export function playMemeSong(mode: string, meme?: string): void {
stopEffectAudio();
let src = '/brother.m4a';
if (meme === 'paisa' || mode === 'paisa') {
src = '/paisa hi paisa hoga.m4a';
} else if (meme === 'nothing' || mode === 'nothing') {
src = '/nothing.m4a';
}
// PAUSE background music immediately while meme plays
if (bgAudio) {
bgAudio.pause();
}
const effect = new Audio(src);
effect.volume = 1.0;
effect.muted = muted;
currentEffectAudio = effect;
const resumeBg = () => {
currentEffectAudio = null;
if (bgAudio && !muted) {
bgAudio.play().catch(() => {});
}
};
effect.onended = resumeBg;
effect.onerror = resumeBg;
effect.play().catch(() => { resumeBg(); });
}
3. High-Resolution Story Card Exporter (src/lib/story.ts)
Client-side rendering of 1080×1920 PNG cards with custom logo drawing and typography:
export async function renderStoryCard(spec: StorySpec): Promise<Blob> {
await fontsReady();
const logoImg = await loadLogo();
const canvas = document.createElement('canvas');
canvas.width = 1080;
canvas.height = 1920;
const ctx = canvas.getContext('2d')!;
// Render background, dots, header pills, and amount readouts...
// Footer branding
if (logoImg) {
ctx.drawImage(logoImg, 1080 / 2 - 50, 1920 - 240, 100, 100);
}
ctx.fillStyle = PALETTE.marigold;
ctx.font = display(38, 700);
ctx.fillText('anithor bond · Rakhi with Digital Love', 1080 / 2, 1920 - 128);
ctx.fillStyle = '#9C8B7E';
ctx.font = body(26, 600);
ctx.fillText("A bond that protects, a love that connects · Don't forget to tag @susantgamerz in insta 📸", 1080 / 2, 1920 - 76);
return new Promise<Blob>((resolve, reject) => {
canvas.toBlob((blob) => (blob ? resolve(blob) : reject(new Error('Export failed'))), 'image/png');
});
}
Technical Challenges and Solutions
Challenge 1: Excessively Long Encrypted URL Hash Links
- Problem: Adding uncompressed Base64 QR payment images to payloads produced 26,000+ character URLs, triggering warnings in WhatsApp.
- Solution: Built
compressQrImageinsrc/lib/upi.tsto scale uploaded QR images down to max 220px JPEG at 0.5 quality before encrypting. - Result: Capsule links reduced to ~2.5kB, operating safely below messaging app limits.
Challenge 2: Background Music and Meme Audio Overlapping
- Problem: When unmuting audio while opening a vault, background music and meme songs played simultaneously, creating a double-audio glitch.
- Solution: Updated
toggleMute()and audio trigger callbacks insrc/lib/audio.tsto pause background music during meme playback and verify!currentEffectAudiobefore unmuting background music. - Result: Pristine single-channel audio transition with automatic background music restoration.
How to Run & Deploy the Project
1. Prerequisites
- Node.js (v18 or higher)
2. Installation
# Install dependencies
npm install
3. Launch Development Server
npm run dev
Open your browser to http://localhost:5173.
4. Build Production Distribution
npm run build
Lessons Learned
- Client-Side Cryptography Enables Pure Serverless Freedom: Combining browser-native Web Crypto API (AES-GCM) with binary stream compression (
deflate-raw) lets complex stateful applications exist entirely within shared links without hosting backend databases. - Image Dimension Scaling is Essential for URL State: Downscaling user-uploaded images to 220px JPEG before Base64 encoding is the difference between a broken 26kB link and a portable 2.5kB URL capsule.
- Dedicated Audio Coordination Prevents Mobile Glitches: Explicitly pausing background channels during sound effect playback ensures reliable mobile browser audio performance across iOS and Android devices.
Future Enhancements
- Multi-Sibling Group Invoices: Support pooled sibling tax requests across multiple sisters or brothers.
- Spatial Positional Audio: Add 3D audio pan attenuation to rotating Thali sweets and physical vault dials.
- Custom Voice Note Encoder: In-browser WebRTC audio recorder with compressed payload encapsulation.
Conclusion
Building anithor bond proved that modern WebGL graphics and cryptography can turn digital gifting into an unforgettable emotional experience.
My sister Sujita got a unique, interactive 3D Rakhi filled with jokes, custom mithai, and a playful Sibling Tax bill—and developers got an open-source, zero-server WebGL reference app.
- Live Website: https://anithor.site
- Developer Portfolio: https://kantarajluitel.tech