# Abdul Rafay — rafay.sh > Personal site of Abdul Rafay, fullstack engineer at ImagineArt. Writing on frontend and backend engineering, project write-ups, and an open shadcn registry of React components and hooks. - Canonical page: https://rafay.sh/ - Agent index: https://rafay.sh/llms.txt - Component registry: https://rafay.sh/r/registry.json ## About Abdul Rafay is a fullstack engineer at ImagineArt, working on core features in OmniAgent and AI chat: code previews, custom widgets and artifacts, and speech-to-text / text-to-speech with cross-browser support. Alongside that he builds SaaS products and internal tools as a freelancer — a law firm portal with role-based access, payments and scheduled jobs, and a multi-branch currency exchange system with complex multi-transaction logic. He designs as well as builds, benches 100kg, and writes about what breaks along the way. ## Experience ### imagine art #### frontend engineer — jan 2026 - present (full time) - working on core chat experience, along with features & improvements in omniagent. - implemented code previews, custom widgets and artifacts from scratch. - shipped speech to text and text to speech with cross browser support. - building echo, the fastest way to create starts with your voice. Stack: typescript, next.js, react.js, tanstack query, shadcn, pnpm ### freelance #### fullstack engineer — sep 2024 - present (full time) - working as a fullstack engineer building web applications including saas and internal tools. - developed a law firm portal with role based access, payments, scheduled jobs, and case related workflows. - built a currency exchange system with support for complex transaction and multi-transaction logic. Stack: typescript, javascript, next.js, react.js, bun, express, postgres, mongo, tanstack query, prisma, tailwind, shadcn, hono, aws, hetzner, digital ocean, figma ## Writing - [Integrating Speech to Text & Text to Speech](https://rafay.sh/blogs/integrating-speech-to-text-and-text-to-speech.md): Integrating STT & TTS into a website seemed simple at first (STT actually was), but there were a few things I didn’t realize needed extra handling. The goal of this blog is to mak… (2026-02-05, tags: TypeScript, Speech to Text, Text to Speech, Web Development) - [WebSockets 101](https://rafay.sh/blogs/websockets-101.md): This blog covers the fundamentals of WebSockets, why they are needed, and how real time communication works on the web. The goal is to help you get started by understanding WebSoc… (2025-12-14, tags: WebSockets, TypeScript, Bun, Web Development) - [Getting Started with Zod](https://rafay.sh/blogs/getting-started-with-zod.md): A beginner friendly guide to validating data in TypeScript using Zod’s powerful schema system—making your apps safer, cleaner, and more predictable. (2024-07-23, tags: TypeScript, Zod, Web Development) All posts: https://rafay.sh/blogs.md ## Chunks Short, unpolished notes. - [fafo](https://rafay.sh/chunks/fafo.md): i recently met a guy for the first time irl, on a trip. somewhere in the conversation he asked me, "what's your biggest/craziest fafo?" i d… All chunks: https://rafay.sh/chunks.md ## Projects ### capyclip — saas (someday) a tool that turns long videos into viral clips using ai powered moment detection and trendy templates - Role: founding engineer - Stack: next.js, react.js, typescript, bun, aws s3, express.js, postgresql, prisma, tailwind css, shadcn, figma - Links: website: https://www.capyclip.com - Details: https://rafay.sh/projects/capyclip.md ### ryu — dev tool (someday) a lightweight, type-safe schema validator for typescript with easy validation, type inference, and safe parsing - Role: core developer - Stack: typescript, bun - Links: github: https://github.com/abdulrafay-07/ryu · npm: https://www.npmjs.com/package/ryu-ts - Details: https://rafay.sh/projects/ryu.md ### currency exchange — internal tool (completed) a currency exchange system with multi branch support, secure roles, advanced transactions, and real-time analytics - Role: full stack - Stack: next.js, react.js, typescript, bun, hono, postgresql, prisma, tailwind css, shadcn - Details: https://rafay.sh/projects/currency-exchange.md ### law firm portal — internal tool (completed) a law firm portal with secure roles, case management, hearing schedules, documents, payments, and client dashboards - Role: full stack - Stack: next.js, react.js, typescript, bun, hono, aws s3, postgresql, prisma, tailwind css, shadcn - Details: https://rafay.sh/projects/law-firm-portal.md ### simt — agency/studio (completed) a storytelling and editing studio that transforms raw clips into cinematic, scroll-stopping content with creative direction and a hint of جنون - Role: design & dev - Stack: next.js, react.js, typescript, figma, tailwind css, shadcn - Links: website: https://www.simt.pk - Details: https://rafay.sh/projects/simt.md ## Stack typescript, javascript, next.js, react.js, bun, hono, express, postgres, mongo, tanstack query, prisma, tailwind, shadcn, aws, hetzner, digital ocean, convex, appwrite, go, figma ## Components & hooks Published as a shadcn registry at https://rafay.sh/r/registry.json. - [Avatar Group](https://rafay.sh/shenanigans/components/avatar-group.md): A component for displaying multiple avatars with tooltips, borders, and spacing. Built on top of Avatar and Tooltip from shadcn UI. — install with `npx shadcn@latest add https://rafay.sh/r/avatar-group.json` - [useConfirm](https://rafay.sh/shenanigans/hooks/use-confirm.md): A hook that shows a confirmation dialog and returns a promise that resolves when the user accepts or rejects. Perfect for delete or irreversible actions. — install with `npx shadcn@latest add https://rafay.sh/r/use-confirm.json` - [useCopy](https://rafay.sh/shenanigans/hooks/use-copy.md): A hook that copies text to the clipboard with optional toast and success/error states. Also includes a built-in, customizable CopyButton component. — install with `npx shadcn@latest add https://rafay.sh/r/use-copy.json` ## Fragments Events and moments, written up. - [devfest karachi 2025](https://rafay.sh/fragments/devfest-karachi-2025.md): dec 20, 2025 ## Elsewhere - [Guestbook](https://rafay.sh/guestbook.md): a wall of one-liners from people who passed through - [Stats](https://rafay.sh/stats.md): the public numbers behind this site ## Contact - [twitter](https://x.com/rafayontren) - [github](https://github.com/abdulrafay-07) - [linkedin](https://www.linkedin.com/in/rafayontren) --- # Terminal — rafay.sh > A shell that drives the whole of rafay.sh. Every page, post, project and registry item is reachable from a command line, and `cat` reads the very markdown you are reading now. - Canonical page: https://rafay.sh/terminal - Commands: 32 ## How it works The site's route tree is presented as a filesystem — `/blogs`, `/chunks`, `/projects`, `/fragments`, `/shenanigans/components`, `/shenanigans/hooks` — so `ls`, `cd` and `cat` behave the way you would expect. `cat ` fetches that page's markdown representation, which means the terminal is a human-facing client for the same surface agents use. A command can be deep-linked: https://rafay.sh/terminal?c=cat+blogs runs it on load. ## Commands ### content - `about`: who abdul rafay is, in full - `id` (also `me`, `whoami`, `fafo`): name, role, and the bits that matter - `blogs`: long-form engineering writing - `chunks`: short, unpolished notes - `projects`: things i have shipped - `shenanigans`: an open shadcn registry - `components`: react components, installable - `hooks`: react hooks, installable - `fragments`: events and moments - `skills` (also `stack`): the stack, as a list - `experience` (also `work`, `cv`): where i have worked and what i did there - `contact` (also `socials`, `links`): where to find me - `guestbook [-n count] [--sort newest|oldest|popular]` (also `who`, `wall`): read the guestbook - `sign "" [--as ]`: leave a line in the guestbook - `stats` (also `top`): the public numbers behind this site ### navigation - `ls [-l] [path]`: list what is in a directory - `cd [path]`: change directory - `pwd`: print the current directory - `cat `: print a page as markdown - `read ` (also `less`, `more`): open a page in the pager - `tree [path]`: show the whole site as a tree - `find ` (also `search`, `grep`): search every page by title, tag and description - `open [path]`: leave the terminal and open the real page ### shell - `help [command]` (also `?`, `commands`): list every command - `theme [name]` (also `themes`): switch the colour scheme - `clear`: wipe the scrollback - `history`: show what you have run - `date`: print the current time - `neofetch`: the obligatory system summary - `banner`: reprint the greeting - `man `: show the manual for one command - `exit` (also `quit`, `logout`): go back to the normal site ## Keys - `tab` completes commands and paths, twice lists the candidates - `↑` / `↓` walk the history - `→` accepts the inline suggestion - `ctrl+l` clears, `ctrl+c` abandons the line - inside the pager: `j`/`k` scroll, `space` pages, `/` finds, `g`/`G` jump to the ends, `q` closes ## Themes The `theme` command offers every colour scheme on the site, terminal or not. The choice is the site's theme, not a terminal-local one, so it persists everywhere — and arriving at the terminal leaves whatever you already had alone. ## Elsewhere on this site - [Home](https://rafay.sh/index.md) - [Writing](https://rafay.sh/blogs.md) - [Chunks](https://rafay.sh/chunks.md) - [Projects](https://rafay.sh/projects.md) - [Components & hooks](https://rafay.sh/shenanigans.md) - [Fragments](https://rafay.sh/fragments.md) - [Guestbook](https://rafay.sh/guestbook.md) - [Stats](https://rafay.sh/stats.md) - [Agent index (llms.txt)](https://rafay.sh/llms.txt) - [Sitemap](https://rafay.sh/sitemap.xml) --- # Blogs — rafay.sh > Long-form writing by Abdul Rafay: things he has learned, broken, fixed, and explored across frontend, backend, and everything in between. - Canonical page: https://rafay.sh/blogs ## Posts - [Integrating Speech to Text & Text to Speech](https://rafay.sh/blogs/integrating-speech-to-text-and-text-to-speech.md): Integrating STT & TTS into a website seemed simple at first (STT actually was), but there were a few things I didn’t realize needed extra handling. The goal of this blog is to mak… (2026-02-05, tags: TypeScript, Speech to Text, Text to Speech, Web Development) - [WebSockets 101](https://rafay.sh/blogs/websockets-101.md): This blog covers the fundamentals of WebSockets, why they are needed, and how real time communication works on the web. The goal is to help you get started by understanding WebSoc… (2025-12-14, tags: WebSockets, TypeScript, Bun, Web Development) - [Getting Started with Zod](https://rafay.sh/blogs/getting-started-with-zod.md): A beginner friendly guide to validating data in TypeScript using Zod’s powerful schema system—making your apps safer, cleaner, and more predictable. (2024-07-23, tags: TypeScript, Zod, Web Development) ## Elsewhere on this site - [Home](https://rafay.sh/index.md) - [Chunks](https://rafay.sh/chunks.md) - [Projects](https://rafay.sh/projects.md) - [Components & hooks](https://rafay.sh/shenanigans.md) - [Fragments](https://rafay.sh/fragments.md) - [Terminal](https://rafay.sh/terminal.md) - [Guestbook](https://rafay.sh/guestbook.md) - [Stats](https://rafay.sh/stats.md) - [Agent index (llms.txt)](https://rafay.sh/llms.txt) - [Sitemap](https://rafay.sh/sitemap.xml) --- # Integrating Speech to Text & Text to Speech > Integrating STT & TTS into a website seemed simple at first (STT actually was), but there were a few things I didn’t realize needed extra handling. The goal of this blog is to make you aware of those issues so that if you’re ever integrating TTS, you don’t run into the same problems. - Author: Abdul Rafay - Published: 2026-02-05 - Updated: 2026-02-05 - Tags: TypeScript, Speech to Text, Text to Speech, Web Development - Canonical page: https://rafay.sh/blogs/integrating-speech-to-text-and-text-to-speech ## Context So I had to integrate STT (Speech to text) TTS (Text to Speech) into the website. The backend was responsible for returning text for STT and streaming binary audio data for TTS. Sounds simple, right? ## Speech to Text The implementation of STT was pretty straightforward. After some browsing, I decided to use the **MediaRecorder Web API** to record audio and store the audio chunks. Once the recording finishes, the chunks are sent to the backend, which returns the transcribed text that’s then shown to the user. ![Simplified Diagram](https://res.cloudinary.com/dyhub4ung/image/upload/v1770286233/blogs/ww17vuu1yhlq0yr669vt.png) ## Text to Speech TTS also seemed straightforward at first. The plan was to use the **MediaSource Web API** to collect audio streams. As soon as the first audio chunk arrives, I’d start playback using an `HTMLAudioElement`, while `MediaSource` keeps appending incoming chunks. That’s exactly what I did, and it worked perfectly. ## Aftermath Things looked great until I got feedback and found out that MediaSource isn’t supported consistently across browsers. On some browsers like Firefox, and even Safari/IOS. TTS wasn’t working at all. After digging a bit more, it turned out this was due to differences in browser audio architectures and autoplay policies. ## Solution There were two separate issues that needed to be fixed: 1. MediaSource not supported by the browser 2. Safari/IOS issue where MediaSource was supported, but audio still didn’t play For the first issue, I added a fallback: if `MediaSource` isn’t supported, the frontend waits for all audio chunks, combines them, and then plays the audio normally. For the second issue, Safari/IOS requires explicit user interaction to allow audio playback. While the existing solution worked fine for shorter responses, larger messages failed. The reason was that longer TTS responses took more time for the backend to send the first chunk, and by the time the frontend received it, the user interaction window had already expired. To fix this, as soon as the play function was triggered, I added a Safari/iOS check and immediately called `audio.play()`, even if no audio data was available yet. ## Elsewhere on this site - [Home](https://rafay.sh/index.md) - [Chunks](https://rafay.sh/chunks.md) - [Projects](https://rafay.sh/projects.md) - [Components & hooks](https://rafay.sh/shenanigans.md) - [Fragments](https://rafay.sh/fragments.md) - [Terminal](https://rafay.sh/terminal.md) - [Guestbook](https://rafay.sh/guestbook.md) - [Stats](https://rafay.sh/stats.md) - [Agent index (llms.txt)](https://rafay.sh/llms.txt) - [Sitemap](https://rafay.sh/sitemap.xml) --- # WebSockets 101 > This blog covers the fundamentals of WebSockets, why they are needed, and how real time communication works on the web. The goal is to help you get started by understanding WebSockets, their basic implementation, and useful resources to explore further. - Author: Abdul Rafay - Published: 2025-12-14 - Updated: 2025-12-15 - Tags: WebSockets, TypeScript, Bun, Web Development - Canonical page: https://rafay.sh/blogs/websockets-101 ## The Problem WebSockets Solve Before talking about WebSocket and how they work, the first question we should ask is: **why do we even need them?** To answer that, we need to briefly understand how HTTP works (which you probably already know if you’re here). At a high level, HTTP follows a request-response model: - The client sends a request - The server processes it and sends back a response Once the response is sent, the communication is essentially over. This is the problem... The client always has to initiate the communication. But what if the server wants to send data to client on its own? It cannot directly push data to the the client. It must wait for the client to ask again. This limitation is exactly where WebSockets come into play. ### Common Workaround (Jugaad) A common workaround you might think of is **polling**. That is, essentially calling an API after every X seconds or minutes. ```typescript setInterval(() => { fetch("/api/notifications") }, 2000); ``` While this may work, it has clear downsides: - Unnecessary server load - Wasted network requests - Delayed “real time” updates This approach does not scale well and is inefficient for truly real time applications. ## What is a WebSocket? Now that we understand the problem, let's answer the question: **What is a WebSocket?** A **WebSocket** is a communication protocol that provides a full duplex (two way/bi-directional), persistent connection between a client and a server, allowing for instant, two way data exchange without repeatedly creating new HTTP requests. In simpler terms: > A WebSocket is a persistent, two way communication channel between a client and a server. ## How WebSockets Work Even with WebSockets, the client still initiates the communication. The difference is how the connection evolves. The initial request is a normal **HTTP request** where the client asks the server to upgrade the connection to WebSockets. ```http GET /ws HTTP/1.1 Host: rafay.sh Upgrade: websocket Connection: Upgrade ``` If the server supports WebSockets, it responds with: ```http HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade ``` The 101 Switching Protocols status means the server agrees to change the communication protocol and from this point onward HTTP is no longer used and connection remains open until either the client or server closes it ## Implementation This is a simple example of an anonymous WebSocket based chat application built using **Bun’s native WebSocket server**. #### Backend It uses in-memory rooms with no database and demonstrates core concepts such as persistent connections, message broadcasting, and connection lifecycle handling. The implementation is intentionally minimal to focus on how real time communication works using WebSockets. ```typescript import type { Server } from "bun"; interface Room { id: number; connections: { userId: string; }[]; }; const TOTAL_CONNECTIONS_PER_ROOM = 3; let ROOM_ID = 0; const rooms: Room[] = []; function getAvailableRoom() { for (let room of rooms) { if (room.connections.length < TOTAL_CONNECTIONS_PER_ROOM) return room; }; const newRoom: Room = { id: ROOM_ID++, connections: [], }; rooms.push(newRoom); return newRoom; }; function getUserConnectedRoom(userId: string) { for (let room of rooms) { if (room.connections.find(c => c.userId === userId)) { return room; }; }; }; function broadcastRoomUsers(room: Room, server: Server<{ userId: string; }>) { const payload = { type: "room_users", users: room.connections.map(c => c.userId), }; server.publish(room.id.toString(), JSON.stringify(payload)); }; const server = Bun.serve({ fetch(req, server) { const url = new URL(req.url); if (url.pathname === "/ws") { const userId = url.searchParams.get("userId"); if (!userId) return new Response("Missing userId", { status: 400 }); const success = server.upgrade(req, { data: { userId: userId, }, }); if (success) return; return new Response("Upgrade failed", { status: 500 }); }; }, websocket: { data: {} as { userId: string }, open: (ws) => { const room = getAvailableRoom(); room.connections.push({ userId: ws.data.userId, }); ws.subscribe(room.id.toString()); broadcastRoomUsers(room, server); }, message(ws, message) { const room = getUserConnectedRoom(ws.data.userId); if (!room) return; const payload = JSON.stringify({ type: "chat", userId: ws.data.userId, message: message.toString(), timestamp: Date.now(), }); // Send message to everyone in the room ws.publish(room?.id.toString(), payload); // Send message to the user itself ws.send(payload); }, close: (ws, code, message) => { const room = getUserConnectedRoom(ws.data.userId); if (!room) return; room.connections = room.connections.filter(c => c.userId !== ws.data.userId); broadcastRoomUsers(room, server); }, }, }); console.log(`Listening on ${server.url}`); ``` #### Frontend I used AI to generate the frontend. Since this blog is focused on **WebSockets** and real time communication, we won’t go into the frontend in detail. However, I’ve included the code here for reference if you want to see how the client connects, sends messages, and handles updates. ```html Chat Room

Connected Users

``` ```html Chat Room

Connected Users

``` ## Relevant Links - [Bun's native websockets docs](https://bun.com/docs/runtime/http/websockets) - [MDN WebSockets API](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) - [Socket.io](https://socket.io/) ## Elsewhere on this site - [Home](https://rafay.sh/index.md) - [Chunks](https://rafay.sh/chunks.md) - [Projects](https://rafay.sh/projects.md) - [Components & hooks](https://rafay.sh/shenanigans.md) - [Fragments](https://rafay.sh/fragments.md) - [Terminal](https://rafay.sh/terminal.md) - [Guestbook](https://rafay.sh/guestbook.md) - [Stats](https://rafay.sh/stats.md) - [Agent index (llms.txt)](https://rafay.sh/llms.txt) - [Sitemap](https://rafay.sh/sitemap.xml) --- # Getting Started with Zod > A beginner friendly guide to validating data in TypeScript using Zod’s powerful schema system—making your apps safer, cleaner, and more predictable. - Author: Abdul Rafay - Published: 2024-07-23 - Updated: 2025-12-14 - Tags: TypeScript, Zod, Web Development - Canonical page: https://rafay.sh/blogs/getting-started-with-zod **Zod** is a TypeScript-first schema declaration and validation library. What does this mean? It means that you can create schemas that are _type-safe_ while also providing _validation_. When you validate data against a **Zod** schema, it not only checks the data’s structure but also returns a typed object that TypeScript can understand. This ensures that your data conforms to the expected types and improves overall code reliability. You might have noticed that I used the word “schema” multiple times. It broadly refers to any data type, from a simple number to a complex nested object. ### Schemas To better understand how schemas work, let’s create a simple schema using Zod. ```typescript import { z } from 'zod' const stringSchema = z.string() stringSchema.parse('zod') // returns 'zod' since it is a string stringSchema.parse(12) // throws an error ``` Zod also provides a way to coerce values: ```typescript import { z } from 'zod' const stringSchema = z.coerce.string() stringSchema.parse(12) // coerces and returns '12' ``` Let’s take a real world example and create a schema for the sign up form. ```typescript import { z } from 'zod' export const signUpSchema = z.object({ username: z.string().min(3).max(20), email: z.email(), password: z.string().min(6), }) ``` You can even add customized error messages to your schema. ```typescript export const signUpSchema = z.object({ username: z .string() .min(3, "Username must have 3 characters") .max(20, "Username cannot exceed 20 characters"), email: z.email(), password: z.string().min(6), }) ``` #### Learn more about Zod: [Installation](https://zod.dev/?id=installation) [Primitives](https://zod.dev/?id=primitives) The most commonly used data types are string and number. Here is a link to Zod’s guide to a handful of validations for each of them. [Strings](https://zod.dev/?id=strings) [Numbers](https://zod.dev/?id=numbers) ## Elsewhere on this site - [Home](https://rafay.sh/index.md) - [Chunks](https://rafay.sh/chunks.md) - [Projects](https://rafay.sh/projects.md) - [Components & hooks](https://rafay.sh/shenanigans.md) - [Fragments](https://rafay.sh/fragments.md) - [Terminal](https://rafay.sh/terminal.md) - [Guestbook](https://rafay.sh/guestbook.md) - [Stats](https://rafay.sh/stats.md) - [Agent index (llms.txt)](https://rafay.sh/llms.txt) - [Sitemap](https://rafay.sh/sitemap.xml) --- # Chunks — rafay.sh > Short notes by Abdul Rafay — pieces of stuff he writes. Raw, unpolished, and his. - Canonical page: https://rafay.sh/chunks ## Chunks - [fafo](https://rafay.sh/chunks/fafo.md): i recently met a guy for the first time irl, on a trip. somewhere in the conversation he asked me, "what's your biggest/craziest fafo?" i didn't have an answer… (2026-08-07) ## Elsewhere on this site - [Home](https://rafay.sh/index.md) - [Writing](https://rafay.sh/blogs.md) - [Projects](https://rafay.sh/projects.md) - [Components & hooks](https://rafay.sh/shenanigans.md) - [Fragments](https://rafay.sh/fragments.md) - [Terminal](https://rafay.sh/terminal.md) - [Guestbook](https://rafay.sh/guestbook.md) - [Stats](https://rafay.sh/stats.md) - [Agent index (llms.txt)](https://rafay.sh/llms.txt) - [Sitemap](https://rafay.sh/sitemap.xml) --- # fafo - Written: 2026-08-07 - Canonical page: https://rafay.sh/chunks/fafo i recently met a guy for the first time irl, on a trip. somewhere in the conversation he asked me, "what's your biggest/craziest fafo?" i didn't have an answer, and that made me ponder during the whole trip. i talk about fafo a lot. but sitting there, i couldn't point to one crazy story. so i started wondering if i even fafo at all, or if i just like the word. then it hit me. the trip itself was the fafo. i found out about it ~2 days before it happened. i had a submission due the same day of the trip. i had to lie to a strict professor to submit early, and i had to convince six other people that leaving early and risking that professor's wrath was worth it. all of that, decided and executed in about 12 hours. if any one of those pieces had gone wrong, i wouldn't have even been on that trip to get asked the question in the first place. so i did have an answer. i just didn't recognize it, maybe because it worked out. i remembered the trip clearly, i just never thought of it as a gamble. maybe that's the trick with fafo, once it pays off, your brain quietly reclassifies it as a good decision instead of what it actually was. but that wasn’t enough, i think there are two kinds of fafo. the acute and the compound. when you hear the word fafo, you think of the crazy shit, which makes sense. acute ones are usually rare (maybe i have skill issue), these are the ones that have short term results, the kind that hit you fast enough to stick in memory right away. however, most of the fafo we do is compound, not acute. these are long term, done step by step, making them add up into something with a serious impact. if you were to think about them in the moment, they won't feel important, because they're not crazy, neither big at the time. i went to the gym consistently for 3 years, never skipped, not even during exams, just lowered the days when things got hectic. no single day felt like a big decision, showing up was just something i did. but three years of just showing up gave me strength, a bunch of people i met along the way, a hobby i can actually talk about, and enough understanding of exercises to know what works for which body. none of that came from one day. it came from compounding. ## Elsewhere on this site - [Home](https://rafay.sh/index.md) - [Writing](https://rafay.sh/blogs.md) - [Projects](https://rafay.sh/projects.md) - [Components & hooks](https://rafay.sh/shenanigans.md) - [Fragments](https://rafay.sh/fragments.md) - [Terminal](https://rafay.sh/terminal.md) - [Guestbook](https://rafay.sh/guestbook.md) - [Stats](https://rafay.sh/stats.md) - [Agent index (llms.txt)](https://rafay.sh/llms.txt) - [Sitemap](https://rafay.sh/sitemap.xml) --- # Projects — rafay.sh > Things Abdul Rafay has built, shipped, trashed, and rebuilt: SaaS products, dev tools, internal tools, and client work. - Canonical page: https://rafay.sh/projects ## Projects - [capyclip](https://rafay.sh/projects/capyclip.md): a tool that turns long videos into viral clips using ai powered moment detection and trendy templates (saas, someday) - [ryu](https://rafay.sh/projects/ryu.md): a lightweight, type-safe schema validator for typescript with easy validation, type inference, and safe parsing (dev tool, someday) - [currency exchange](https://rafay.sh/projects/currency-exchange.md): a currency exchange system with multi branch support, secure roles, advanced transactions, and real-time analytics (internal tool, completed) - [law firm portal](https://rafay.sh/projects/law-firm-portal.md): a law firm portal with secure roles, case management, hearing schedules, documents, payments, and client dashboards (internal tool, completed) - [simt](https://rafay.sh/projects/simt.md): a storytelling and editing studio that transforms raw clips into cinematic, scroll-stopping content with creative direction and a hint of جنون (agency/studio, completed) ## Elsewhere on this site - [Home](https://rafay.sh/index.md) - [Writing](https://rafay.sh/blogs.md) - [Chunks](https://rafay.sh/chunks.md) - [Components & hooks](https://rafay.sh/shenanigans.md) - [Fragments](https://rafay.sh/fragments.md) - [Terminal](https://rafay.sh/terminal.md) - [Guestbook](https://rafay.sh/guestbook.md) - [Stats](https://rafay.sh/stats.md) - [Agent index (llms.txt)](https://rafay.sh/llms.txt) - [Sitemap](https://rafay.sh/sitemap.xml) --- # capyclip > an ai video repurposing tool that finds the best moments from podcasts or videos, auto-creates short viral reels, and applies popular templates—helping you produce more content in minutes - Category: saas - Role: Abdul Rafay — founding engineer - Status: someday (stopped atm) - Stack: next.js, react.js, typescript, bun, aws s3, express.js, postgresql, prisma, tailwind css, shadcn, figma - Website: https://www.capyclip.com - Canonical page: https://rafay.sh/projects/capyclip ## Write-up will write about it when i get time, sorry :p ## Elsewhere on this site - [Home](https://rafay.sh/index.md) - [Writing](https://rafay.sh/blogs.md) - [Chunks](https://rafay.sh/chunks.md) - [Components & hooks](https://rafay.sh/shenanigans.md) - [Fragments](https://rafay.sh/fragments.md) - [Terminal](https://rafay.sh/terminal.md) - [Guestbook](https://rafay.sh/guestbook.md) - [Stats](https://rafay.sh/stats.md) - [Agent index (llms.txt)](https://rafay.sh/llms.txt) - [Sitemap](https://rafay.sh/sitemap.xml) --- # ryu > a typescript validation library where users can define schemas for strings, numbers, booleans, objects, and arrays, validate inputs, infer types, and handle errors safely - Category: dev tool - Role: Abdul Rafay — core developer - Status: someday (whenever i feel like working) - Stack: typescript, bun - GitHub: https://github.com/abdulrafay-07/ryu - npm: https://www.npmjs.com/package/ryu-ts - Canonical page: https://rafay.sh/projects/ryu ## Write-up will write about it when i get time, sorry :p ## Elsewhere on this site - [Home](https://rafay.sh/index.md) - [Writing](https://rafay.sh/blogs.md) - [Chunks](https://rafay.sh/chunks.md) - [Components & hooks](https://rafay.sh/shenanigans.md) - [Fragments](https://rafay.sh/fragments.md) - [Terminal](https://rafay.sh/terminal.md) - [Guestbook](https://rafay.sh/guestbook.md) - [Stats](https://rafay.sh/stats.md) - [Agent index (llms.txt)](https://rafay.sh/llms.txt) - [Sitemap](https://rafay.sh/sitemap.xml) --- # currency exchange > a multi-branch currency exchange platform with role-based access, single and multi-sub transactions, currency management, printable receipts, and detailed financial dashboards for full operational visibility - Category: internal tool - Role: Abdul Rafay — full stack - Status: completed - Stack: next.js, react.js, typescript, bun, hono, postgresql, prisma, tailwind css, shadcn - Canonical page: https://rafay.sh/projects/currency-exchange ## Write-up will write about it when i get time, sorry :p ## Elsewhere on this site - [Home](https://rafay.sh/index.md) - [Writing](https://rafay.sh/blogs.md) - [Chunks](https://rafay.sh/chunks.md) - [Components & hooks](https://rafay.sh/shenanigans.md) - [Fragments](https://rafay.sh/fragments.md) - [Terminal](https://rafay.sh/terminal.md) - [Guestbook](https://rafay.sh/guestbook.md) - [Stats](https://rafay.sh/stats.md) - [Agent index (llms.txt)](https://rafay.sh/llms.txt) - [Sitemap](https://rafay.sh/sitemap.xml) --- # law firm portal > a role-based law firm system for managing cases, hearings, documents, and payments. includes client dashboards, automated reminders, and admin analytics for full visibility. - Category: internal tool - Role: Abdul Rafay — full stack - Status: completed - Stack: next.js, react.js, typescript, bun, hono, aws s3, postgresql, prisma, tailwind css, shadcn - Canonical page: https://rafay.sh/projects/law-firm-portal ## Write-up will write about it when i get time, sorry :p ## Elsewhere on this site - [Home](https://rafay.sh/index.md) - [Writing](https://rafay.sh/blogs.md) - [Chunks](https://rafay.sh/chunks.md) - [Components & hooks](https://rafay.sh/shenanigans.md) - [Fragments](https://rafay.sh/fragments.md) - [Terminal](https://rafay.sh/terminal.md) - [Guestbook](https://rafay.sh/guestbook.md) - [Stats](https://rafay.sh/stats.md) - [Agent index (llms.txt)](https://rafay.sh/llms.txt) - [Sitemap](https://rafay.sh/sitemap.xml) --- # simt > a creative studio that shapes your story into polished, un-skippable visuals—turning shaky clips into cinematic edits that make your brand impossible to ignore - Category: agency/studio - Role: Abdul Rafay — design & dev - Status: completed - Stack: next.js, react.js, typescript, figma, tailwind css, shadcn - Website: https://www.simt.pk - Canonical page: https://rafay.sh/projects/simt ## Write-up will write about it when i get time, sorry :p ## Elsewhere on this site - [Home](https://rafay.sh/index.md) - [Writing](https://rafay.sh/blogs.md) - [Chunks](https://rafay.sh/chunks.md) - [Components & hooks](https://rafay.sh/shenanigans.md) - [Fragments](https://rafay.sh/fragments.md) - [Terminal](https://rafay.sh/terminal.md) - [Guestbook](https://rafay.sh/guestbook.md) - [Stats](https://rafay.sh/stats.md) - [Agent index (llms.txt)](https://rafay.sh/llms.txt) - [Sitemap](https://rafay.sh/sitemap.xml) --- # Shenanigans — rafay.sh > Abdul Rafay's playground of reusable React components and hooks, pulled out of real projects and published as a shadcn registry. - Canonical page: https://rafay.sh/shenanigans - Registry index: https://rafay.sh/r/registry.json ## Sections - [Components](https://rafay.sh/shenanigans/components.md): reusable React components - [Hooks](https://rafay.sh/shenanigans/hooks.md): reusable React hooks ## Install any item Every item is installable with the shadcn CLI, no account or key required: ```bash npx shadcn@latest add https://rafay.sh/r/avatar-group.json ``` ## Elsewhere on this site - [Home](https://rafay.sh/index.md) - [Writing](https://rafay.sh/blogs.md) - [Chunks](https://rafay.sh/chunks.md) - [Projects](https://rafay.sh/projects.md) - [Fragments](https://rafay.sh/fragments.md) - [Terminal](https://rafay.sh/terminal.md) - [Guestbook](https://rafay.sh/guestbook.md) - [Stats](https://rafay.sh/stats.md) - [Agent index (llms.txt)](https://rafay.sh/llms.txt) - [Sitemap](https://rafay.sh/sitemap.xml) --- # Components — rafay.sh > Reusable React components from Abdul Rafay's shadcn registry, built with React, Next.js, Tailwind, and shadcn/ui primitives. - Canonical page: https://rafay.sh/shenanigans/components - Registry index: https://rafay.sh/r/registry.json ## Components - [Avatar Group](https://rafay.sh/shenanigans/components/avatar-group.md): A component for displaying multiple avatars with tooltips, borders, and spacing. Built on top of Avatar and Tooltip from shadcn UI. — `npx shadcn@latest add https://rafay.sh/r/avatar-group.json` ## Elsewhere on this site - [Home](https://rafay.sh/index.md) - [Writing](https://rafay.sh/blogs.md) - [Chunks](https://rafay.sh/chunks.md) - [Projects](https://rafay.sh/projects.md) - [Fragments](https://rafay.sh/fragments.md) - [Terminal](https://rafay.sh/terminal.md) - [Guestbook](https://rafay.sh/guestbook.md) - [Stats](https://rafay.sh/stats.md) - [Agent index (llms.txt)](https://rafay.sh/llms.txt) - [Sitemap](https://rafay.sh/sitemap.xml) --- # Avatar Group > A component for displaying multiple avatars with tooltips, borders, and spacing. Built on top of Avatar and Tooltip from shadcn UI. - Type: React component - Install: `npx shadcn@latest add https://rafay.sh/r/avatar-group.json` - shadcn dependencies: avatar, tooltip - Registry item: https://rafay.sh/r/avatar-group.json - Canonical page: https://rafay.sh/shenanigans/components/avatar-group ## Documented on the page - Installation - Usage - Props - Examples - Spacing - Tooltip - Border & Max - Hover & Translate ## Elsewhere on this site - [Home](https://rafay.sh/index.md) - [Writing](https://rafay.sh/blogs.md) - [Chunks](https://rafay.sh/chunks.md) - [Projects](https://rafay.sh/projects.md) - [Fragments](https://rafay.sh/fragments.md) - [Terminal](https://rafay.sh/terminal.md) - [Guestbook](https://rafay.sh/guestbook.md) - [Stats](https://rafay.sh/stats.md) - [Agent index (llms.txt)](https://rafay.sh/llms.txt) - [Sitemap](https://rafay.sh/sitemap.xml) --- # Hooks — rafay.sh > Reusable React hooks from Abdul Rafay's shadcn registry, used in real projects. Clean, typed, and easy to drop in. - Canonical page: https://rafay.sh/shenanigans/hooks - Registry index: https://rafay.sh/r/registry.json ## Hooks - [useConfirm](https://rafay.sh/shenanigans/hooks/use-confirm.md): Shows a confirmation dialog and returns a promise that resolves when the user accepts or rejects. Perfect for delete or irreversible actions. — `npx shadcn@latest add https://rafay.sh/r/use-confirm.json` - [useCopy](https://rafay.sh/shenanigans/hooks/use-copy.md): Copies text to the clipboard with optional toast and success/error states. Also includes a built-in, customizable CopyButton component. — `npx shadcn@latest add https://rafay.sh/r/use-copy.json` ## Elsewhere on this site - [Home](https://rafay.sh/index.md) - [Writing](https://rafay.sh/blogs.md) - [Chunks](https://rafay.sh/chunks.md) - [Projects](https://rafay.sh/projects.md) - [Fragments](https://rafay.sh/fragments.md) - [Terminal](https://rafay.sh/terminal.md) - [Guestbook](https://rafay.sh/guestbook.md) - [Stats](https://rafay.sh/stats.md) - [Agent index (llms.txt)](https://rafay.sh/llms.txt) - [Sitemap](https://rafay.sh/sitemap.xml) --- # useConfirm > Shows a confirmation dialog and returns a promise that resolves when the user accepts or rejects. Perfect for delete or irreversible actions. - Type: React hook - Tags: ui/ux, utility, confirmation - Install: `npx shadcn@latest add https://rafay.sh/r/use-confirm.json` - shadcn dependencies: dialog, card, button - npm dependencies: class-variance-authority - Registry item: https://rafay.sh/r/use-confirm.json - Canonical page: https://rafay.sh/shenanigans/hooks/use-confirm ## Signature ```ts const [DeleteDialog, confirm] = useConfirm( "Are you sure?", "This will delete all of the files from this project", "destructive", ); ``` ## Elsewhere on this site - [Home](https://rafay.sh/index.md) - [Writing](https://rafay.sh/blogs.md) - [Chunks](https://rafay.sh/chunks.md) - [Projects](https://rafay.sh/projects.md) - [Fragments](https://rafay.sh/fragments.md) - [Terminal](https://rafay.sh/terminal.md) - [Guestbook](https://rafay.sh/guestbook.md) - [Stats](https://rafay.sh/stats.md) - [Agent index (llms.txt)](https://rafay.sh/llms.txt) - [Sitemap](https://rafay.sh/sitemap.xml) --- # useCopy > Copies text to the clipboard with optional toast and success/error states. Also includes a built-in, customizable CopyButton component. - Type: React hook - Tags: utility, clipboard - Install: `npx shadcn@latest add https://rafay.sh/r/use-copy.json` - shadcn dependencies: sonner, button - npm dependencies: class-variance-authority - Registry item: https://rafay.sh/r/use-copy.json - Canonical page: https://rafay.sh/shenanigans/hooks/use-copy ## Signature ```ts const { CopyButton, copied, copy, error } = useCopy({ showToast: true, successMessage: "Copied!", }); ``` ## Elsewhere on this site - [Home](https://rafay.sh/index.md) - [Writing](https://rafay.sh/blogs.md) - [Chunks](https://rafay.sh/chunks.md) - [Projects](https://rafay.sh/projects.md) - [Fragments](https://rafay.sh/fragments.md) - [Terminal](https://rafay.sh/terminal.md) - [Guestbook](https://rafay.sh/guestbook.md) - [Stats](https://rafay.sh/stats.md) - [Agent index (llms.txt)](https://rafay.sh/llms.txt) - [Sitemap](https://rafay.sh/sitemap.xml) --- # Fragments — rafay.sh > Days, memories, and moments Abdul Rafay captured: talks, events, and everything in between. - Canonical page: https://rafay.sh/fragments ## Fragments - [devfest karachi 2025](https://rafay.sh/fragments/devfest-karachi-2025.md): dec 20, 2025 ## Elsewhere on this site - [Home](https://rafay.sh/index.md) - [Writing](https://rafay.sh/blogs.md) - [Chunks](https://rafay.sh/chunks.md) - [Projects](https://rafay.sh/projects.md) - [Components & hooks](https://rafay.sh/shenanigans.md) - [Terminal](https://rafay.sh/terminal.md) - [Guestbook](https://rafay.sh/guestbook.md) - [Stats](https://rafay.sh/stats.md) - [Agent index (llms.txt)](https://rafay.sh/llms.txt) - [Sitemap](https://rafay.sh/sitemap.xml) --- # devfest karachi 2025 - Date: dec 20, 2025 - Canonical page: https://rafay.sh/fragments/devfest-karachi-2025 #### Dec 20th, 2025 ![nastp](https://res.cloudinary.com/dyhub4ung/image/upload/v1766348033/nastp_uotzus.png) # devfest 2025 by gdg this saturday, i went to a tech event hosted by gdg kolachi at nastp. it was my first time there, and honestly, the place was pretty cool. right after entering, i got my attendee card and some swags (a mug, some stickers to be exact). i took the elevator up to the conference room. it was pretty big, but the lights on the stage were insanely bright and kept hitting me in the eyes. after settling in, i ran into a few people from my university and caught up with them for a bit. ![conference room](https://res.cloudinary.com/dyhub4ung/image/upload/v1766430299/devfest_p3n0vq.jpg) then there was a coffee break in between. outside, i noticed a photo booth (the thing where you take a picture and get a polaroid). i wanted to get one, but the line was huge, so i waited and came back later during the sessions and got the polaroid. ![swags and polaroid](https://res.cloudinary.com/dyhub4ung/image/upload/v1766431929/swags_or6d1g.png) my favourite session was by wajahat karim (“shipped > perfect“). he talked about how shipping early and iteratively improving your product with user feedback is better than not launching your product until it is perfect (because it'll never be perfect). he also shared pre-launch, launch and post-launch checklists. overall, a really good session. ![shipped > perfect](https://res.cloudinary.com/dyhub4ung/image/upload/v1766430298/shipped_devfest_kkwesl.jpg) after that, i met more people and had some nice conversations. i even got ended up in a reel that gdg's social team was filming (they were featuring the best outfits at the event 😭). ps: this is me shamelessy signing my lifting acc (on which i post rarely) ![rvfaylifts](https://res.cloudinary.com/dyhub4ung/image/upload/v1766431554/Snapchat-1289374411_eakeee.jpg) ## Elsewhere on this site - [Home](https://rafay.sh/index.md) - [Writing](https://rafay.sh/blogs.md) - [Chunks](https://rafay.sh/chunks.md) - [Projects](https://rafay.sh/projects.md) - [Components & hooks](https://rafay.sh/shenanigans.md) - [Terminal](https://rafay.sh/terminal.md) - [Guestbook](https://rafay.sh/guestbook.md) - [Stats](https://rafay.sh/stats.md) - [Agent index (llms.txt)](https://rafay.sh/llms.txt) - [Sitemap](https://rafay.sh/sitemap.xml) --- # Guestbook — rafay.sh > A wall of one-liners from people who passed through rafay.sh. One signature per visitor per day, 140 characters, no links. - Canonical page: https://rafay.sh/guestbook - Signatures: 1 ## How to sign Use the form on the page, or the shell at https://rafay.sh/terminal: ``` sign "hello from tokyo" --as vyro ``` Visitors who give no name get a stable anonymous handle. The wall records the theme each person signed in and paints a swatch of it. ## The wall Newest first. https://rafay.sh/api/guestbook serves the same list as JSON and takes `?sort=newest|oldest|popular` — `popular` is by like count, and a visitor may like an entry once. - **rafayontren** (pts/1, 2026-09-13, og + regular): fafo. ## Elsewhere on this site - [Home](https://rafay.sh/index.md) - [Writing](https://rafay.sh/blogs.md) - [Chunks](https://rafay.sh/chunks.md) - [Projects](https://rafay.sh/projects.md) - [Components & hooks](https://rafay.sh/shenanigans.md) - [Fragments](https://rafay.sh/fragments.md) - [Terminal](https://rafay.sh/terminal.md) - [Stats](https://rafay.sh/stats.md) - [Agent index (llms.txt)](https://rafay.sh/llms.txt) - [Sitemap](https://rafay.sh/sitemap.xml) --- # Stats — rafay.sh > The public numbers behind rafay.sh: visitors, views, likes, the themes people pick and the commands they run in the terminal. Aggregates only, rebuilt hourly. - Canonical page: https://rafay.sh/stats - Generated: 2026-09-13T18:42:19.117Z ## Totals - Visitors: 92 - Visits: 186 - Visits in the last 30 days: 7 - Content views: 56 - Likes: 13 - Guestbook signatures: 1 ## Most read 1. [fafo](https://rafay.sh/chunks/fafo.md) — 32 views 2. [Integrating Speech to Text & Text to Speech](https://rafay.sh/blogs/integrating-speech-to-text-and-text-to-speech.md) — 13 views 3. [WebSockets 101](https://rafay.sh/blogs/websockets-101.md) — 7 views 4. [Getting Started with Zod](https://rafay.sh/blogs/getting-started-with-zod.md) — 4 views ## Most liked 1. [fafo](https://rafay.sh/chunks/fafo.md) — 8 likes 2. [Integrating Speech to Text & Text to Speech](https://rafay.sh/blogs/integrating-speech-to-text-and-text-to-speech.md) — 4 likes 3. [WebSockets 101](https://rafay.sh/blogs/websockets-101.md) — 1 likes ## Themes in use - light: 2 - rosé pine moon: 1 - dark: 1 - paper: 1 ## Commands run in the terminal - `help`: 2 - `stats`: 2 - `exit`: 2 - `neofetch`: 1 - `guestbook`: 1 - `id`: 1 - `theme`: 1 ## What is not collected No IP addresses, no third-party analytics identity, and no per-visitor data in these numbers. A first-party `rsh_vid` cookie counts a visit and holds likes; it never leaves the server. ## Elsewhere on this site - [Home](https://rafay.sh/index.md) - [Writing](https://rafay.sh/blogs.md) - [Chunks](https://rafay.sh/chunks.md) - [Projects](https://rafay.sh/projects.md) - [Components & hooks](https://rafay.sh/shenanigans.md) - [Fragments](https://rafay.sh/fragments.md) - [Terminal](https://rafay.sh/terminal.md) - [Guestbook](https://rafay.sh/guestbook.md) - [Agent index (llms.txt)](https://rafay.sh/llms.txt) - [Sitemap](https://rafay.sh/sitemap.xml)