<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Rishabh Rao</title><description>Rishabh Rao is a software engineer in Mumbai, working on agentic video at invideo. Previously codedamn. Lately, IoT projects with ESP32s.</description><link>https://rao.dev</link><language>en-us</language><item><title>Trips - shareable multi-stop planner</title><link>https://rao.dev/blog/trips</link><guid isPermaLink="true">https://rao.dev/blog/trips</guid><description>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.</description><pubDate>Sat, 04 Apr 2026 00:00:00 GMT</pubDate><content:encoded>import Figure from &quot;~/components/Figure.astro&quot;;
import LiveEmbed from &quot;~/components/LiveEmbed.astro&quot;;
import TripReorderDemo from &quot;~/components/TripReorderDemo.astro&quot;;
import plannerDark from &quot;~/assets/blog/trips/planner-dark.png&quot;;
import plannerLight from &quot;~/assets/blog/trips/planner-light.png&quot;;
import plannerMobileDark from &quot;~/assets/blog/trips/planner-mobile-dark.png&quot;;
import plannerMobileLight from &quot;~/assets/blog/trips/planner-mobile-light.png&quot;;
import shareDark from &quot;~/assets/blog/trips/share-dark.png&quot;;
import shareLight from &quot;~/assets/blog/trips/share-light.png&quot;;
import shareMobileDark from &quot;~/assets/blog/trips/share-mobile-dark.png&quot;;
import shareMobileLight from &quot;~/assets/blog/trips/share-mobile-light.png&quot;;

export const demoTrip =
	&quot;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&amp;m=0%3Awalk%2C1%3Ametro%2C2%3Ataxi&quot;;

I was planning a day out in Mumbai with friends and needed three things in one
place: what time we were at each stop, how we were getting between them, and a
way to send the whole thing to everyone. Google Maps does the route but not the
schedule. A WhatsApp message does the schedule but not the map. A shared doc
does neither.

So I vibe coded one in an evening: a single HTML file, no build step, no
backend, no accounts.

&lt;LiveEmbed
	src={demoTrip}
	poster={plannerLight}
	posterDark={plannerDark}
	posterMobile={plannerMobileLight}
	posterMobileDark={plannerMobileDark}
	label=&quot;Load the live app&quot;
	alt=&quot;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.&quot;
/&gt;

## The plan is the URL

The requirement that decided everything else was sharing. Not sharing as a
feature with an invite flow, just: my friends need to open this. The only thing
everyone can already open is a link.

So there is no database. The trip is the query string. Here are the first two
stops of the day above, with the spaces decoded so it is readable:

```
?s=Gateway%20of%20India%2C%20A%20Ward|18.92196|72.83456|2026-04-04T08:00,
   Hutatma%20Chowk%2C%20Fort|18.93274|72.83163|2026-04-04T08:30
&amp;m=0:walk,1:metro,2:taxi
```

`s` is the stops, pipe separated: name, latitude, longitude, time. Each name is
percent-encoded before the stops are joined, which is why the comma inside
&quot;Gateway of India, A Ward&quot; does not split the list. `m` maps each gap between
two stops to a transport mode, keyed on the index of the stop it leaves from.

Every edit calls `history.pushState`, which is where undo comes from. The back
button is the undo button. That is half the reason state went into the URL and
not into memory: no undo stack to maintain, no shortcut to teach anyone, and it
works the same on a phone. Delete a stop by mistake, press back, it is there
again along with the mode that went with it.

```js
window.addEventListener(&quot;popstate&quot;, () =&gt; {
	_skipPush = true;
	stateFromURL();
	render();
});
```

The `_skipPush` flag stops the restore from pushing a fresh entry on top of the
one it just came from.

## Reordering a day is not reordering a list

The obvious way to move a stop is the one-liner:

```js
const [item] = waypoints.splice(from, 1);
waypoints.splice(to, 0, item);
```

The time sits on the stop, so it travels with it. That is correct for a list of
objects and wrong for a plan of a day.

When you drag Malabar Hill above Marine Drive you are not saying &quot;move 11:30 to
Malabar Hill&quot;. You are saying &quot;let&apos;s be at Malabar Hill at 09:00 instead&quot;. The
time belongs to the slot in the day, not to the place. Try it both ways:

&lt;TripReorderDemo&gt;

```
naive splice              what it does now
08:00  Gateway of India   08:00  Gateway of India
08:30  Hutatma Chowk      08:30  Hutatma Chowk
11:30  Malabar Hill       09:00  Malabar Hill
09:00  Marine Drive       11:30  Marine Drive
```

&lt;/TripReorderDemo&gt;

The naive rule produces a day that runs backwards. The fix moves only the
location and writes it back into the fixed slots:

```js
function reorderWaypoint(from, to) {
	// Only move name + coordinates. Times, modes, and slot positions stay fixed.
	const names = waypoints.map((w) =&gt; w.name);
	const lats = waypoints.map((w) =&gt; w.lat);
	const lngs = waypoints.map((w) =&gt; w.lng);
	const [n] = names.splice(from, 1);
	names.splice(to, 0, n);
	const [la] = lats.splice(from, 1);
	lats.splice(to, 0, la);
	const [ln] = lngs.splice(from, 1);
	lngs.splice(to, 0, ln);
	waypoints.forEach((w, j) =&gt; {
		w.name = names[j];
		w.lat = lats[j];
		w.lng = lngs[j];
	});
}
```

Uglier than a two-line splice, and right for what the thing actually is.

## What it deliberately does not do

It draws straight lines between stops, not routes. Routing means an API, a key
and a rate limit, and a plan does not need one: you want the shape of the day,
and two pins with a line between them say enough. When you actually need
directions there is a button that hands the whole trip to Google Maps as
`/maps/dir/lat,lng/lat,lng/`.

Search is Nominatim, which is free and needs no key. 300 ms debounce, results
biased towards whatever the map is currently showing, coordinates rounded to
five decimal places. Five is roughly a metre, which is more precision than a day
plan needs and keeps the link shorter.

&lt;Figure
	src={shareLight}
	dark={shareDark}
	mobile={shareMobileLight}
	mobileDark={shareMobileDark}
	alt=&quot;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&quot;
/&gt;

## Deploying it

Push to `main`, GitHub Actions runs `wrangler pages deploy .`, done. There is no
build step because there is nothing to build. The repo is the artifact. The only
things the page fetches are Leaflet 1.9.4 and CartoDB&apos;s map tiles, and the page
itself is about 14.5 kB gzipped.

It is vibe coded and it looks it: one long file, styles inline on elements, a
helper called `_i()` that returns SVG markup as strings. For something built in
an evening so a group chat could agree on a plan, that was the right trade. The
reorder is the part I actually thought about.</content:encoded><category>trips</category><category>webdev</category><category>maps</category><category>leaflet</category><category>openstreetmap</category><category>url-state</category><category>cloudflare-pages</category></item><item><title>Rdamn - online playground IDEs</title><link>https://rao.dev/blog/rdamn</link><guid isPermaLink="true">https://rao.dev/blog/rdamn</guid><description>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.</description><pubDate>Tue, 15 Mar 2022 00:00:00 GMT</pubDate><content:encoded>import Figure from &quot;~/components/Figure.astro&quot;;
import image1 from &quot;~/assets/blog/rdamn/image1.png&quot;;
import image2 from &quot;~/assets/blog/rdamn/image2.png&quot;;

I had always been very intrigued by
[codedamn&apos;s online playgrounds](https://codedamn.com/playgrounds) and wanted to
figure out how something like that would work. So I decided to try to build it
from scratch on my own.

It took me almost a month to build it but I had implemented almost all the
functionalities that codedamn playgrounds had at that time. I sent my project to
the founder of codedamn who was so impressed by it that he decided to hire me
full-time :)

Here are a couple of screenshots from rdamn:

&lt;Figure src={image1} alt=&quot;Landing page&quot; /&gt;
&lt;Figure src={image2} alt=&quot;Playgrounds interface&quot; /&gt;

This project was definitely the most interesting and complex one I had attempted
till date. I learnt a lot while building it. I had to:

- Implement my own DNS servers for providing nice and friendly urls for client
  connections
- Set up a custom container orchestrator system for safely booting playgrounds
  quickly on demand and shutting them down when the client disconnects
- Build an s3 synchronization service that saves the user&apos;s data to s3 and
  restores from it when a user starts their session after inactivity
- Work with monaco, xterm and other frontend libraries for a good user
  experience

...and many other things that helped me become a better programmer.

Check out rdamn&apos;s GitHub repo to know more about how it works:
[https://github.com/rishabhrao/rdamn](https://github.com/rishabhrao/rdamn)</content:encoded><category>rdamn</category><category>webdev</category><category>ide</category><category>playground</category><category>codedamn</category></item><item><title>Surveys &amp; Simulations</title><link>https://rao.dev/blog/surveys-and-simulations</link><guid isPermaLink="true">https://rao.dev/blog/surveys-and-simulations</guid><description>An easy-to-use platform to help training professionals engage with learners more effectively. It makes training more engaging for trainers.</description><pubDate>Fri, 21 Jan 2022 00:00:00 GMT</pubDate><content:encoded>import Figure from &quot;~/components/Figure.astro&quot;;
import image1 from &quot;~/assets/blog/surveys-and-simulations/image1.png&quot;;
import image2 from &quot;~/assets/blog/surveys-and-simulations/image2.png&quot;;
import image3 from &quot;~/assets/blog/surveys-and-simulations/image3.png&quot;;
import image4 from &quot;~/assets/blog/surveys-and-simulations/image4.png&quot;;

Surveys &amp; Simulations is an online training tool with which trainers can conduct
simulations having a series of quizzes with hundreds of users and then assess
their performance and publish the learnings. It also lets the trainers collect
data from the users with pre and post simulation surveys. This allows
participants to work together as a team and gain a better understanding of
theoretical business concepts. It boosts participant involvement and enjoyment.
Simulation has been shown in studies to improve participant knowledge retention,
decision-making, and teamwork skills.

Here are a few screenshots from Surveys &amp; Simulations:

&lt;Figure src={image1} alt=&quot;Landing page&quot; /&gt;
&lt;Figure src={image2} alt=&quot;Registration page&quot; /&gt;
&lt;Figure src={image3} alt=&quot;Trainer dashboard&quot; /&gt;
&lt;Figure src={image4} alt=&quot;Post-workshop summary for user&quot; /&gt;

I built this project when I was working with
[Bigtime Consulting Private Limited](https://www.linkedin.com/company/bigtimeconsulting/)
on a contract to build a few educational websites.</content:encoded><category>surveys-and-simulations</category><category>webdev</category><category>surveys</category><category>simulations</category><category>training</category></item><item><title>R Visual</title><link>https://rao.dev/blog/rvisual</link><guid isPermaLink="true">https://rao.dev/blog/rvisual</guid><description>A pathfinding algorithm visualizer that animates BFS, DFS, Dijkstra&apos;s and A* search across a 2D grid so you can watch each algorithm work.</description><pubDate>Sat, 02 Jan 2021 00:00:00 GMT</pubDate><content:encoded>import Figure from &quot;~/components/Figure.astro&quot;;
import image1 from &quot;~/assets/blog/rvisual/image1.png&quot;;
import image2 from &quot;~/assets/blog/rvisual/image2.png&quot;;
import image3 from &quot;~/assets/blog/rvisual/image3.png&quot;;

A pathfinding algorithm seeks to find the shortest path between two points. This
application visualizes various pathfinding algorithms in action, and more...

R Visual supports 4 algorithms: Breadth first search (BFS), Depth first search
(DFS), Dijkstra&apos;s and A\* search algorithm. It lets the user choose a start
point and end point in a 2D grid array and also lets the user add multiple
obstacles in the path. It uses the selected algorithm to find a path from the
start point to the end point. The user can trace the path that the algorithm is
following to learn more about how the algorithm works.

Here are a few screenshots from R Visual:

&lt;Figure src={image1} alt=&quot;Welcome tutorial&quot; /&gt;
&lt;Figure src={image2} alt=&quot;Main grid interface&quot; /&gt;
&lt;Figure
	src={image3}
	alt=&quot;Solution showing the shortest path when obstacles are preset&quot;
/&gt;

I built this project when I was learning DSA (Data Structures &amp; Algorithms) and
exploring some real life uses of these algorithms.</content:encoded><category>rvisual</category><category>dsa</category><category>algorithms</category><category>path-finding</category><category>dijkstra</category><category>astar</category><category>bfs</category><category>breadth-first-search</category><category>dfs</category><category>depth-first-search</category></item></channel></rss>