{
  "version": "https://jsonfeed.org/version/1.1",
  "title": "Rishabh Rao",
  "home_page_url": "https://rao.dev",
  "feed_url": "https://rao.dev/feed.json",
  "description": "Rishabh Rao is a software engineer in Mumbai, working on agentic video at invideo. Previously codedamn. Lately, IoT projects with ESP32s.",
  "language": "en-US",
  "authors": [
    {
      "name": "Rishabh Rao",
      "url": "https://rao.dev"
    }
  ],
  "items": [
    {
      "id": "https://rao.dev/blog/trips",
      "url": "https://rao.dev/blog/trips",
      "title": "Trips - shareable multi-stop planner",
      "summary": "Plan multi-stop trips with transport modes like car, metro, flight and more. Drop pins on a map, reorder stops, share your itinerary via link, and export directions to Google Maps. Free, no sign-up.",
      "content_text": "import Figure from \"~/components/Figure.astro\";\nimport LiveEmbed from \"~/components/LiveEmbed.astro\";\nimport TripReorderDemo from \"~/components/TripReorderDemo.astro\";\nimport plannerDark from \"~/assets/blog/trips/planner-dark.png\";\nimport plannerLight from \"~/assets/blog/trips/planner-light.png\";\nimport plannerMobileDark from \"~/assets/blog/trips/planner-mobile-dark.png\";\nimport plannerMobileLight from \"~/assets/blog/trips/planner-mobile-light.png\";\nimport shareDark from \"~/assets/blog/trips/share-dark.png\";\nimport shareLight from \"~/assets/blog/trips/share-light.png\";\nimport shareMobileDark from \"~/assets/blog/trips/share-mobile-dark.png\";\nimport shareMobileLight from \"~/assets/blog/trips/share-mobile-light.png\";\n\nexport const demoTrip =\n\t\"https://trips.rao.dev/?s=Gateway%2520of%2520India%252C%2520A%2520Ward%7C18.92196%7C72.83456%7C2026-04-04T08%253A00%2CHutatma%2520Chowk%252C%2520Fort%7C18.93274%7C72.83163%7C2026-04-04T08%253A30%2CMarine%2520Drive%252C%2520Fort%7C18.93281%7C72.82347%7C2026-04-04T09%253A00%2CMalabar%2520Hill%252C%2520Malabar%2520Hill%7C18.95816%7C72.80337%7C2026-04-04T11%253A30&m=0%3Awalk%2C1%3Ametro%2C2%3Ataxi\";\n\nI was planning a day out in Mumbai with friends and needed three things in one\nplace: what time we were at each stop, how we were getting between them, and a\nway to send the whole thing to everyone. Google Maps does the route but not the\nschedule. A WhatsApp message does the schedule but not the map. A shared doc\ndoes neither.\n\nSo I vibe coded one in an evening: a single HTML file, no build step, no\nbackend, no accounts.\n\n<LiveEmbed\n\tsrc={demoTrip}\n\tposter={plannerLight}\n\tposterDark={plannerDark}\n\tposterMobile={plannerMobileLight}\n\tposterMobileDark={plannerMobileDark}\n\tlabel=\"Load the live app\"\n\talt=\"A morning in south Mumbai: Gateway of India at 08:00, Hutatma Chowk at 08:30, Marine Drive at 09:00, Malabar Hill at 11:30, walking then metro then taxi between them.\"\n/>\n\n## The plan is the URL\n\nThe requirement that decided everything else was sharing. Not sharing as a\nfeature with an invite flow, just: my friends need to open this. The only thing\neveryone can already open is a link.\n\nSo there is no database. The trip is the query string. Here are the first two\nstops of the day above, with the spaces decoded so it is readable:\n\n```\n?s=Gateway%20of%20India%2C%20A%20Ward|18.92196|72.83456|2026-04-04T08:00,\n   Hutatma%20Chowk%2C%20Fort|18.93274|72.83163|2026-04-04T08:30\n&m=0:walk,1:metro,2:taxi\n```\n\n`s` is the stops, pipe separated: name, latitude, longitude, time. Each name is\npercent-encoded before the stops are joined, which is why the comma inside\n\"Gateway of India, A Ward\" does not split the list. `m` maps each gap between\ntwo stops to a transport mode, keyed on the index of the stop it leaves from.\n\nEvery edit calls `history.pushState`, which is where undo comes from. The back\nbutton is the undo button. That is half the reason state went into the URL and\nnot into memory: no undo stack to maintain, no shortcut to teach anyone, and it\nworks the same on a phone. Delete a stop by mistake, press back, it is there\nagain along with the mode that went with it.\n\n```js\nwindow.addEventListener(\"popstate\", () => {\n\t_skipPush = true;\n\tstateFromURL();\n\trender();\n});\n```\n\nThe `_skipPush` flag stops the restore from pushing a fresh entry on top of the\none it just came from.\n\n## Reordering a day is not reordering a list\n\nThe obvious way to move a stop is the one-liner:\n\n```js\nconst [item] = waypoints.splice(from, 1);\nwaypoints.splice(to, 0, item);\n```\n\nThe time sits on the stop, so it travels with it. That is correct for a list of\nobjects and wrong for a plan of a day.\n\nWhen you drag Malabar Hill above Marine Drive you are not saying \"move 11:30 to\nMalabar Hill\". You are saying \"let's be at Malabar Hill at 09:00 instead\". The\ntime belongs to the slot in the day, not to the place. Try it both ways:\n\n<TripReorderDemo>\n\n```\nnaive splice              what it does now\n08:00  Gateway of India   08:00  Gateway of India\n08:30  Hutatma Chowk      08:30  Hutatma Chowk\n11:30  Malabar Hill       09:00  Malabar Hill\n09:00  Marine Drive       11:30  Marine Drive\n```\n\n</TripReorderDemo>\n\nThe naive rule produces a day that runs backwards. The fix moves only the\nlocation and writes it back into the fixed slots:\n\n```js\nfunction reorderWaypoint(from, to) {\n\t// Only move name + coordinates. Times, modes, and slot positions stay fixed.\n\tconst names = waypoints.map((w) => w.name);\n\tconst lats = waypoints.map((w) => w.lat);\n\tconst lngs = waypoints.map((w) => w.lng);\n\tconst [n] = names.splice(from, 1);\n\tnames.splice(to, 0, n);\n\tconst [la] = lats.splice(from, 1);\n\tlats.splice(to, 0, la);\n\tconst [ln] = lngs.splice(from, 1);\n\tlngs.splice(to, 0, ln);\n\twaypoints.forEach((w, j) => {\n\t\tw.name = names[j];\n\t\tw.lat = lats[j];\n\t\tw.lng = lngs[j];\n\t});\n}\n```\n\nUglier than a two-line splice, and right for what the thing actually is.\n\n## What it deliberately does not do\n\nIt draws straight lines between stops, not routes. Routing means an API, a key\nand a rate limit, and a plan does not need one: you want the shape of the day,\nand two pins with a line between them say enough. When you actually need\ndirections there is a button that hands the whole trip to Google Maps as\n`/maps/dir/lat,lng/lat,lng/`.\n\nSearch is Nominatim, which is free and needs no key. 300 ms debounce, results\nbiased towards whatever the map is currently showing, coordinates rounded to\nfive decimal places. Five is roughly a metre, which is more precision than a day\nplan needs and keeps the link shorter.\n\n<Figure\n\tsrc={shareLight}\n\tdark={shareDark}\n\tmobile={shareMobileLight}\n\tmobileDark={shareMobileDark}\n\talt=\"The share dialog, showing the trip as plain text with arrows between stops, the full link, and buttons to copy text, copy link, share, or open in Google Maps\"\n/>\n\n## Deploying it\n\nPush to `main`, GitHub Actions runs `wrangler pages deploy .`, done. There is no\nbuild step because there is nothing to build. The repo is the artifact. The only\nthings the page fetches are Leaflet 1.9.4 and CartoDB's map tiles, and the page\nitself is about 14.5 kB gzipped.\n\nIt is vibe coded and it looks it: one long file, styles inline on elements, a\nhelper called `_i()` that returns SVG markup as strings. For something built in\nan evening so a group chat could agree on a plan, that was the right trade. The\nreorder is the part I actually thought about.",
      "date_published": "2026-04-04T00:00:00.000Z",
      "date_modified": "2026-04-04T00:00:00.000Z",
      "tags": [
        "trips",
        "webdev",
        "maps",
        "leaflet",
        "openstreetmap",
        "url-state",
        "cloudflare-pages"
      ],
      "image": "https://rao.dev/og/blog/trips.png",
      "external_url": "https://rao.dev/blog/trips.md"
    },
    {
      "id": "https://rao.dev/blog/rdamn",
      "url": "https://rao.dev/blog/rdamn",
      "title": "Rdamn - online playground IDEs",
      "summary": "A fully featured online IDE service that allows anyone to quickly boot up and run Next.JS, Express or any other projects without any effort.",
      "content_text": "import Figure from \"~/components/Figure.astro\";\nimport image1 from \"~/assets/blog/rdamn/image1.png\";\nimport image2 from \"~/assets/blog/rdamn/image2.png\";\n\nI had always been very intrigued by\n[codedamn's online playgrounds](https://codedamn.com/playgrounds) and wanted to\nfigure out how something like that would work. So I decided to try to build it\nfrom scratch on my own.\n\nIt took me almost a month to build it but I had implemented almost all the\nfunctionalities that codedamn playgrounds had at that time. I sent my project to\nthe founder of codedamn who was so impressed by it that he decided to hire me\nfull-time :)\n\nHere are a couple of screenshots from rdamn:\n\n<Figure src={image1} alt=\"Landing page\" />\n<Figure src={image2} alt=\"Playgrounds interface\" />\n\nThis project was definitely the most interesting and complex one I had attempted\ntill date. I learnt a lot while building it. I had to:\n\n- Implement my own DNS servers for providing nice and friendly urls for client\n  connections\n- Set up a custom container orchestrator system for safely booting playgrounds\n  quickly on demand and shutting them down when the client disconnects\n- Build an s3 synchronization service that saves the user's data to s3 and\n  restores from it when a user starts their session after inactivity\n- Work with monaco, xterm and other frontend libraries for a good user\n  experience\n\n...and many other things that helped me become a better programmer.\n\nCheck out rdamn's GitHub repo to know more about how it works:\n[https://github.com/rishabhrao/rdamn](https://github.com/rishabhrao/rdamn)",
      "date_published": "2022-03-15T00:00:00.000Z",
      "date_modified": "2022-03-15T00:00:00.000Z",
      "tags": [
        "rdamn",
        "webdev",
        "ide",
        "playground",
        "codedamn"
      ],
      "image": "https://rao.dev/og/blog/rdamn.png",
      "external_url": "https://rao.dev/blog/rdamn.md"
    },
    {
      "id": "https://rao.dev/blog/surveys-and-simulations",
      "url": "https://rao.dev/blog/surveys-and-simulations",
      "title": "Surveys & Simulations",
      "summary": "An easy-to-use platform to help training professionals engage with learners more effectively. It makes training more engaging for trainers.",
      "content_text": "import Figure from \"~/components/Figure.astro\";\nimport image1 from \"~/assets/blog/surveys-and-simulations/image1.png\";\nimport image2 from \"~/assets/blog/surveys-and-simulations/image2.png\";\nimport image3 from \"~/assets/blog/surveys-and-simulations/image3.png\";\nimport image4 from \"~/assets/blog/surveys-and-simulations/image4.png\";\n\nSurveys & Simulations is an online training tool with which trainers can conduct\nsimulations having a series of quizzes with hundreds of users and then assess\ntheir performance and publish the learnings. It also lets the trainers collect\ndata from the users with pre and post simulation surveys. This allows\nparticipants to work together as a team and gain a better understanding of\ntheoretical business concepts. It boosts participant involvement and enjoyment.\nSimulation has been shown in studies to improve participant knowledge retention,\ndecision-making, and teamwork skills.\n\nHere are a few screenshots from Surveys & Simulations:\n\n<Figure src={image1} alt=\"Landing page\" />\n<Figure src={image2} alt=\"Registration page\" />\n<Figure src={image3} alt=\"Trainer dashboard\" />\n<Figure src={image4} alt=\"Post-workshop summary for user\" />\n\nI built this project when I was working with\n[Bigtime Consulting Private Limited](https://www.linkedin.com/company/bigtimeconsulting/)\non a contract to build a few educational websites.",
      "date_published": "2022-01-21T00:00:00.000Z",
      "date_modified": "2022-01-21T00:00:00.000Z",
      "tags": [
        "surveys-and-simulations",
        "webdev",
        "surveys",
        "simulations",
        "training"
      ],
      "image": "https://rao.dev/og/blog/surveys-and-simulations.png",
      "external_url": "https://rao.dev/blog/surveys-and-simulations.md"
    },
    {
      "id": "https://rao.dev/blog/rvisual",
      "url": "https://rao.dev/blog/rvisual",
      "title": "R Visual",
      "summary": "A pathfinding algorithm visualizer that animates BFS, DFS, Dijkstra's and A* search across a 2D grid so you can watch each algorithm work.",
      "content_text": "import Figure from \"~/components/Figure.astro\";\nimport image1 from \"~/assets/blog/rvisual/image1.png\";\nimport image2 from \"~/assets/blog/rvisual/image2.png\";\nimport image3 from \"~/assets/blog/rvisual/image3.png\";\n\nA pathfinding algorithm seeks to find the shortest path between two points. This\napplication visualizes various pathfinding algorithms in action, and more...\n\nR Visual supports 4 algorithms: Breadth first search (BFS), Depth first search\n(DFS), Dijkstra's and A\\* search algorithm. It lets the user choose a start\npoint and end point in a 2D grid array and also lets the user add multiple\nobstacles in the path. It uses the selected algorithm to find a path from the\nstart point to the end point. The user can trace the path that the algorithm is\nfollowing to learn more about how the algorithm works.\n\nHere are a few screenshots from R Visual:\n\n<Figure src={image1} alt=\"Welcome tutorial\" />\n<Figure src={image2} alt=\"Main grid interface\" />\n<Figure\n\tsrc={image3}\n\talt=\"Solution showing the shortest path when obstacles are preset\"\n/>\n\nI built this project when I was learning DSA (Data Structures & Algorithms) and\nexploring some real life uses of these algorithms.",
      "date_published": "2021-01-02T00:00:00.000Z",
      "date_modified": "2021-01-02T00:00:00.000Z",
      "tags": [
        "rvisual",
        "dsa",
        "algorithms",
        "path-finding",
        "dijkstra",
        "astar",
        "bfs",
        "breadth-first-search",
        "dfs",
        "depth-first-search"
      ],
      "image": "https://rao.dev/og/blog/rvisual.png",
      "external_url": "https://rao.dev/blog/rvisual.md"
    }
  ],
  "_rao": {
    "about": "Every page is also available as markdown by appending .md to its URL. See /llms.txt.",
    "llms_txt": "https://rao.dev/llms.txt",
    "llms_full_txt": "https://rao.dev/llms-full.txt",
    "socials": [
      "https://www.linkedin.com/in/rishabhraos1",
      "https://x.com/rishabhrao",
      "https://github.com/rishabhrao"
    ]
  }
}