DEV Community

Cover image for 🎨 Evolving Darwin: How I made a MVP with vibe coding
Sumit Roy
Sumit Roy

Posted on

10 4 4 3 3

🎨 Evolving Darwin: How I made a MVP with vibe coding

Remember when I told you about my journey building Darwin, that simple HLD designer that started as a rebellion against bloated diagramming tools? Well, plot twist – I've been secretly obsessing over it for last few days, and the app you see today at darwin-topaz.vercel.app is basically Darwin on steroids. But here's the kicker: it's still ridiculously simple to use.

The "Oh Crap, People Actually Use This" Moment

When I posted that DEV blog about Darwin, I figured maybe 5 people would check it out, say "neat" and forget about it.

Instead I wake up to like 28 comments, people suggesting features I should've thought of, and - this was the crazy part - folks actually using it for real interviews. Not just playing around with it, but actual system design interviews.

That's when it clicked. I didn't just build a tool for myself. I accidentally solved a problem other people had too. So what started as a weekend thing to scratch my own itch suddenly became this thing I couldn't stop thinking about.

From Basic Sketch Tool to Something Actually Useful

So where Darwin started vs where it is now - it's pretty different. The first version was basically like drawing on a napkin digitally. You could drag some boxes around, draw arrows between them, export a PNG. That was literally it.

But I've added a bunch of stuff since then, and honestly each feature has its own little story:

Note: The prompts below also needed some modifications after the machine generated the code. Some small bug fixing, but definitely something which improved my productivity by 10x

The Mobile Thing I Completely Missed πŸ“±

Remember how I said people were using Darwin? Yeah, well turns out some of them were trying to use it on their phones. Which... didn't work. At all.

Picture this: you're in an interview trying to explain how Netflix's architecture works while pinching and zooming on a tiny screen. Not exactly the professional vibe you're going for.

So instead of just making the desktop version "responsive" (air quotes because we all know how that usually goes), I went a different route. Built this radial menu thing that pops up when you tap the add button on mobile.

Here's the actual code that creates this magic:

const ringConfig = [
  { radius: 90, itemCount: 4 },
  { radius: 160, itemCount: 5 },
  { radius: 230, itemCount: 6 },
];
const ANGLE_RANGE_DEG = 90;
const START_ANGLE_OFFSET = 10;
// Calculate position for each component in the radial menu
const x = ring.radius * Math.cos(angle * Math.PI / 180);
const y = ring.radius * Math.sin(angle * Math.PI / 180);
// Staggered animation timing for that satisfying cascade effect
const totalDelay = (globalElementIndex * 25);
Enter fullscreen mode Exit fullscreen mode

Prompt: Add a radial menu when someone presses the add button for smaller screens. Keep the icons similar to what we have now for each component.

Smart Responsive Design That Actually Works πŸ₯

But mobile wasn't just about the radial menu. I had to completely rethink how the entire interface adapts. Here's my approach using a custom hook:

const isMobile = useMediaQuery('(max-width: 767px)');
const isDesktop = useMediaQuery('(min-width: 768px)');

// Desktop gets a sidebar drawer, mobile gets the radial menu
if (isDesktop) {
  return <ComponentDrawer />; // Traditional sidebar
}
// Mobile gets the fancy radial menu
return <RadialComponentMenu />;
Enter fullscreen mode Exit fullscreen mode

This wasn't just about screen size – I had to think about touch interactions, finger-friendly button sizes, and how people actually hold their phones. The delete and undo buttons only appear on mobile when you need them, positioned perfectly for your thumb.

Prompt: Add undo and trash icon for smaller screens. Place the trash icon just right of the add button and undo just above the add button.

The Component Library That Actually Makes Sense

You know what was embarrassing? Having a system design tool with like six components. "Hey, design Netflix with just a Load Balancer and Database!" Yeah, not happening.

I've expanded the component library to include all the usual suspects, and here's how I organized them:

export enum ElementType {
  User = 'USER',
  WebClient = 'WEB_CLIENT',
  MobileClient = 'MOBILE_CLIENT',
  ApiGateway = 'API_GATEWAY',
  LoadBalancer = 'LOAD_BALANCER',
  Microservice = 'MICROSERVICE',
  MessageQueue = 'MESSAGE_QUEUE',
  Cache = 'CACHE',
  Database = 'DATABASE',
  CDN = 'CDN',
  AuthService = 'AUTH_SERVICE',
  Search = 'SEARCH',
  ObjectStorage = 'OBJECT_STORAGE',
  TextBox = 'TEXT_BOX',
  Custom = 'CUSTOM',
}
Enter fullscreen mode Exit fullscreen mode

Each component gets its own config with proper colors, icons, and descriptions:

[ElementType.MessageQueue]: {
  icon: <MessageQueueIcon />,
  color: 'bg-orange-100 border-orange-300',
  textColor: 'text-orange-800',
  defaultName: 'Message Queue',
  description: 'An asynchronous communication buffer between different services (e.g., RabbitMQ, Kafka).',
},
Enter fullscreen mode Exit fullscreen mode

The best part? I color-coded everything so you can instantly tell what type of component you're looking at. Message queues are orange, databases are purple, caches are indigo. Your brain starts recognizing patterns without you even thinking about it.

Prompt: Add more components such as cache, queue, object storage, cdn and search

Undo/Redo Like Magic πŸͺ„

This one's embarrassing. I shipped a design tool without undo. Do you know how many perfectly good diagrams I accidentally destroyed while testing? Too many.

But implementing undo/redo properly is trickier than you'd think. Here's my solution using a custom history hook:

export const useHistory = <T>(initialPresent: T) => {
  const [state, setState] = useState<History<T>>({
    past: [],
    present: initialPresent,
    future: [],
  });

  const set = useCallback((newPresent: React.SetStateAction<T>) => {
    setState(s => {
      // Don't create history entries for identical states
      if (JSON.stringify(resolvedNewPresent) === JSON.stringify(s.present)) {
        return s;
      }

      return {
        past: [...s.past, s.present],
        present: resolvedNewPresent,
        future: [], // Clear redo stack when making new changes
      };
    });
  }, []);
};
Enter fullscreen mode Exit fullscreen mode

Now you can Cmd+Z your way out of any mistake, just like in real life. (If only it worked that way everywhere, right?)

Prompt: Add undo functionality if user presses cmd+z by using a history hook

Multi-Select for the efficiency βœ„

Here's where things get fun. I implemented area selection – hold Space and drag to select multiple elements. Want to move your entire backend cluster? Click, drag, done.

const handleDeleteSelected = useCallback(() => {
  if (selectedElementIds.length === 0) return;

  setDiagram(currentDiagram => {
    const idsToDelete = new Set(selectedElementIds);
    const newElements = currentDiagram.elements.filter(el => !idsToDelete.has(el.id));
    const newConnectors = currentDiagram.connectors.filter(conn => 
      !idsToDelete.has(conn.from) && !idsToDelete.has(conn.to)
    );
    return { elements: newElements, connectors: newConnectors };
  });

  setSelectedElementIds([]);
}, [selectedElementIds, setDiagram]);
Enter fullscreen mode Exit fullscreen mode

The selection state automatically cleans up when you undo/redo, so you never end up with ghost selections pointing to elements that don't exist anymore.

Desktop Prompt: Add functionality to select multiple components if user presses space and then drag. Components in this area will get selected.

Mobile Prompt: For smaller screens if user taps on component it should get selected and keep on selecting until it taps on the already selected component

Export That Doesn't Suck πŸš›

The original export was basically "take a screenshot and hope for the best." Now I've got smart cropping that calculates the exact bounds of your diagram:

const handleExportImage = async (format: 'png' | 'jpeg') => {
  const getElWidth = (el: ElementData) => el.width || ELEMENT_CONFIG[el.type].defaultWidth || ELEMENT_DIMENSIONS.width;
  const getElHeight = (el: ElementData) => el.height || ELEMENT_CONFIG[el.type].defaultHeight || ELEMENT_DIMENSIONS.height;

  // Calculate the bounding box of all elements
  const minX = Math.min(...elements.map(el => el.x));
  const minY = Math.min(...elements.map(el => el.y));
  const maxX = Math.max(...elements.map(el => el.x + getElWidth(el)));
  const maxY = Math.max(...elements.map(el => el.y + getElHeight(el)));

  const width = maxX - minX + PADDING * 2;
  const height = maxY - minY + PADDING * 2;
};
Enter fullscreen mode Exit fullscreen mode

No more giant images with 90% whitespace. Your diagram gets exported with perfect cropping and consistent padding.

Prompt: Add export functionality so that user can export in png and jpeg format. Also create a json format which can be exported and can be used in case we import the same.

The Community That Shaped Darwin

The comments on my original post were pure gold. Here are some that actually made it into the app:

@sanskaromar asked for better mobile support – and honestly, that comment alone probably saved Darwin from becoming another "desktop-only" relic.

Someone suggested multi-select features, and it was such an obvious "why isn't this already a thing?" moment that I implemented it immediately.

Multiple people asked about collaboration features and version history. Those are still on my roadmap, but here's the thing – I want to keep Darwin simple. Real-time collaboration means backends, databases, user accounts, and suddenly I'm building Slack for diagrams. That's not the Darwin way.

The Technical Stack (For My Fellow Code Nerds)

Here's what powers Darwin 2.0:

  • React 19 with hooks for everything (useState is my best friend)
  • TypeScript because I learned my lesson about runtime errors the hard way
  • Vite for lightning-fast development (seriously, hot reload is instant)
  • html-to-image for clean PNG/JPEG exports
  • Pure frontend – no backend, no database, no drama

The architecture is beautifully simple:

components/     # UI components and canvas logic
hooks/         # Custom hooks for undo/redo, responsive design  
examples/      # Sample architectures to get started
types.ts       # TypeScript definitions
constants.tsx  # Component configs and styling
Enter fullscreen mode Exit fullscreen mode

One thing I'm particularly proud of is the canvas interaction system. Instead of using a heavy library like Fabric.js or Konva, I built the entire thing with vanilla React and DOM events. It's fast, it's lightweight, and I understand every line of code.

The Drag-and-Drop Magic

Want to see something cool? Here's how I implemented the drag-from-palette feature:

const handlePointerDown = (e: React.PointerEvent, type: ElementType) => {
  const target = e.currentTarget;
  target.setPointerCapture(e.pointerId);

  const handlePointerMove = (moveEvent: PointerEvent) => {
    const dx = Math.abs(moveEvent.clientX - startX);
    const dy = Math.abs(moveEvent.clientY - startY);

    if (dx > 5 || dy > 5) {
      dragInfoRef.current.isDragging = true;
      setDragPreview({ type, x: moveEvent.clientX, y: moveEvent.clientY });
    }
  };

  const handlePointerUp = (upEvent: PointerEvent) => {
    if (dragInfoRef.current.isDragging) {
      // Dispatch custom event to canvas for element creation
      const dropEvent = new CustomEvent('hld-drop', {
        detail: { type: type, clientX: upEvent.clientX, clientY: upEvent.clientY }
      });
      canvasEl.dispatchEvent(dropEvent);
    }
  };
};
Enter fullscreen mode Exit fullscreen mode

It handles mouse, touch, and pen input seamlessly. The drag preview follows your cursor with a subtle pulse animation, and dropping creates the element exactly where you expect it.

Real People, Real Systems, Real usecases

Are you prepping for system design interviews? Darwin is perfect for practicing those classic questions - design Instagram, build a chat system, create a video streaming platform. The components are all there, and you can iterate quickly without getting bogged down in complex UI.
You can even start with some samples like

  • E-commerce platforms with proper load balancing and microservices
  • Social media architectures that would make Twitter jealous
  • Video streaming systems that rival Netflix's complexity

Building a side project and need to document your architecture? Skip the heavyweight tools and sketch it out in Darwin. Export a clean PNG for your README, share it with your team, or use it in presentations. No accounts, no subscriptions, just pure functionality.

Teaching system design concepts? Darwin makes it easy to build diagrams on the fly during lectures or workshops. The mobile-friendly interface means students can follow along on their phones, and the example architectures give you ready-made starting points.

I'd love to see what you build! If you create something cool with Darwin - whether it's a system design for your startup, an interview prep diagram, or just experimenting with architectures - share it! Tag me on social media, post it in the discussions on GitHub, or just drop a comment below.

The Open Source Adventure Begins

Here's where things get interesting. Darwin is now fully open source at github.com/sroy8091/darwin, and I'm both excited and terrified.

The repo includes everything – complete source code, setup instructions, example architectures, and even a proper contributing guide. Want to run it locally? It's literally three commands:

git clone https://github.com/sroy8091/darwin.git
cd darwin  
npm install && npm run dev
Enter fullscreen mode Exit fullscreen mode

Want to contribute? Here's what I'm looking for:

  • New component types (blockchain components, anyone?)
  • Better accessibility features
  • Performance optimizations
  • Mobile interaction improvements
  • Export format additions (SVG support is coming!)

Just check the issues page or create a new one. I'm particularly interested in ideas from people who actually use system design tools in their daily work.

What's Next for Darwin?

I've got a roadmap that's both ambitious and realistic:

Short term (next month):

  • SVG export for crisp vector graphics
  • Keyboard shortcuts for power users
  • Component grouping and templates
  • Better mobile touch interactions

Medium term (next quarter):

  • Flowchart components for algorithm interviews
  • Auto-layout algorithms for cleaner diagrams
  • Component search and filtering
  • Diagram versioning and history

Long term (the dream):

  • System simulation (imagine clicking "play" and watching traffic flow)
  • AI-powered diagram generation from text descriptions
  • Maybe, maybe some light collaboration features

The Philosophy That Keeps Darwin Simple

Here's the thing that keeps me up at night: how do I add features without turning Darwin into the bloated tools I originally hated?

My rule is simple: Every feature must make the core use case better, not just add more stuff.

When someone suggests real-time collaboration, I ask: "Does this help a developer quickly sketch a system architecture?" When they suggest advanced theming, I ask: "Does this help communicate technical concepts better?"

If the answer is no, it doesn't go in Darwin. That's how we stay focused.

Why Darwin Matters (Beyond My Ego)

Look, there are plenty of diagramming tools out there. Lucidchart costs $12/month and has every feature imaginable. Draw.io is free and works fine. Miro is beautiful and collaborative.

But none of them were built specifically for developers creating system designs. They're all generic tools trying to be everything to everyone.

Darwin is different because:

  • It's fast – no loading screens, no account setup, no feature tours
  • It speaks developer – components that actually matter for system design
  • It's mobile-first – works perfectly on phones and tablets
  • It's free forever – no subscriptions, no feature gates, no vendor lock-in
  • It's open source – don't like something? Fix it yourself

The Community I'm Building

This isn't just about a tool anymore. I'm accidentally building a community of developers who care about simple, focused software.

The response to the original blog was incredible. People shared their own frustrations with existing tools, suggested improvements, and even offered to contribute. That's when I knew Darwin was bigger than just my personal itch to scratch.

If you've read this far, you're probably the kind of developer I built Darwin for. You value simplicity over features, you understand that constraints breed creativity, and you believe that tools should get out of your way and let you work.

Try Darwin Today (And Help Me Make It Better)

Want to give it a shot? Head over to darwin-topaz.vercel.app and start dragging some components around. It works in any browser, on any device, no signup required.

Found a bug? Have an idea? The GitHub repo is at github.com/sroy8091/darwin. Issues and pull requests are not just welcome – they're desperately needed.

And if you build something cool with Darwin, tag me on social media or drop a comment below. I love seeing what people create with simple tools.

The Real Lesson Here

Building Darwin taught me something important: sometimes the best way to solve a problem is to ignore everything everyone else is doing and just build what you actually need.

I didn't set out to compete with billion-dollar companies or build the next unicorn startup. I just wanted a simple tool that let me quickly sketch system architectures without fighting the interface.

Turns out, that's exactly what a lot of other developers wanted too.

Darwin isn't trying to be the best diagramming tool in the world. It's trying to be the best system design tool for developers who value simplicity over features.

And honestly? I think we're getting there.


Darwin is open source and looking for contributors! Check out the GitHub repo and let's build something great together. Because the best tools are built by the people who actually use them.

Sentry image

See why 4M developers consider Sentry, β€œnot bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (2)

Collapse
 
om_chimna_f6202ad28fe6986 profile image
Om Chimna β€’

Bro, It's Really... I mean really helpful to So Many.
It's never easier to explain the whole system with this simplicity.
I personally love and encourage sites on the internet that require no unnecessary sign-ups & especially NO-ADS.
I am personally taking this app as part of my contribution, and already have & will come up with many ideas for this
But for now, you can have this as a suggestion...

  • When I Connect An Object With That Arror, I Don't Have Any Mechanism To Remove It Like...

Much appreciated Bro, Keep Doing It πŸ‘πŸ»

Collapse
 
sroy8091 profile image
Sumit Roy β€’

This is really a great idea, consider it done.
Actually it is done. Go ahead and try it out now -> darwin-topaz.vercel.app/

Launch embedded dashboards in 10% of the time - with 100% of your standards.

Launch embedded dashboards in 10% of the time - with 100% of your standards.

Ship pixel-perfect dashboards that feel native to your app with Embeddable. It's fast, flexible, and built for devs.

Get early access

πŸ‘‹ Kindness is contagious

Take a moment to explore this thoughtful article, beloved by the supportive DEV Community. Coders of every background are invited to share and elevate our collective know-how.

A heartfelt "thank you" can brighten someone's dayβ€”leave your appreciation below!

On DEV, sharing knowledge smooths our journey and tightens our community bonds. Enjoyed this? A quick thank you to the author is hugely appreciated.

Okay