WebGL & Zero-Server Cryptography

How I Built anithor bond: A 3D Raksha Bandhan Experience with Three.js & Zero-Server Cryptography

Launch anithor bond Web App 3D brass pooja thali, customizable 3D threads, client-side AES-GCM encryption, and Sibling Tax negotiation.
Launch anithor.site → Read Case Study ↗

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."

anithor bond 3D Raksha Bandhan Experience Cover


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

Invoice Item 2: Secret Keeping Fee

Brother Defense Clause: The Charger Non-Interference Act


The Engineering Challenge

While the concept was joyful, the technical requirements were strictly demanding:

  1. 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.
  2. 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.
  3. 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.
  4. 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.

3D Rakhi Forge & Customization Interface


What I Built

anithor bond is built around two complementary user flows:

  1. 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=).
  2. 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 />).

3D Troll Vault Cracking & Particle FX

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

Challenge 2: Background Music and Meme Audio Overlapping


How to Run & Deploy the Project

1. Prerequisites

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

  1. 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.
  2. 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.
  3. 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


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.

Kanta Raj Luitel

Written by Kanta Raj Luitel

Full-Stack Developer, AI Builder & Cyber Security Researcher from Nepal. Specializing in WebGL shaders, client-side cryptographic systems, and story-driven creative engineering.