<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: kulman Lab</title>
    <description>The latest articles on DEV Community by kulman Lab (@kulman_lab_9ff2748a5207d1).</description>
    <link>https://dev.to/kulman_lab_9ff2748a5207d1</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3958516%2F2a016d98-872f-489f-ad39-1a15a5a952f1.png</url>
      <title>DEV Community: kulman Lab</title>
      <link>https://dev.to/kulman_lab_9ff2748a5207d1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kulman_lab_9ff2748a5207d1"/>
    <language>en</language>
    <item>
      <title>Building a CAD app in the browser</title>
      <dc:creator>kulman Lab</dc:creator>
      <pubDate>Mon, 19 Oct 2026 08:00:38 +0000</pubDate>
      <link>https://dev.to/kulman_lab_9ff2748a5207d1/building-a-cad-app-in-the-browser-3pg8</link>
      <guid>https://dev.to/kulman_lab_9ff2748a5207d1/building-a-cad-app-in-the-browser-3pg8</guid>
      <description>&lt;p&gt;&lt;a href="https://kulmanlab.com" rel="noopener noreferrer"&gt;Kulman&lt;/a&gt; is a 2D CAD editor that runs entirely in the browser. It's an Angular app drawing onto a single &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt;, with no backend at all: drawings live in IndexedDB, and files come in and go out as DXF, the format most CAD tools can exchange.&lt;/p&gt;

&lt;p&gt;Over the last two months it got about 490 commits. This is a tour of what changed and what I learned. Some of it is new features, but a good part is the unglamorous work of making a canvas app survive real drawings and real browsers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Splines you can actually edit
&lt;/h2&gt;

&lt;p&gt;Splines used to be something you could draw and look at. Now you can trim them, trim other things against them, extend lines to them, and hatch inside them, even when a spline crosses itself.&lt;/p&gt;

&lt;p&gt;The interesting decision was how to cut one. The obvious approach is to sample the curve and fit a new spline to each remaining piece, but then the trimmed piece is only &lt;em&gt;close&lt;/em&gt; to where the original was. Instead, a cut is &lt;strong&gt;knot insertion&lt;/strong&gt;: the piece is the same B-spline restated over less of its parameter range, exact to floating-point rounding, and it only costs a few array splices however long the spline is.&lt;/p&gt;

&lt;p&gt;Finding where things meet works the way it does elsewhere in the app: intersections are found on the curve's chords, then pulled onto the true curve with Newton's method. Chords alone miss one case: a curve that dips through a shape and comes back out between two chord ends. Those get followed on the true curve too.&lt;/p&gt;

&lt;p&gt;Performance was the other half. One spline with 5,000 control points used to cost about &lt;strong&gt;150 ms per mouse move&lt;/strong&gt; to pick and &lt;strong&gt;180 ms per frame&lt;/strong&gt; to draw, partly because evaluation scanned for the knot span instead of bisecting. The chords now sit in a tree of boxes in the order they run, so a pick or an intersection costs what's near it rather than the whole curve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hatching that finds its own boundary
&lt;/h2&gt;

&lt;p&gt;Hatch asks for a point and fills whatever region encloses it. Four separate lines that meet at their ends enclose a region just as a closed polyline does, and anything closed inside becomes an island.&lt;/p&gt;

&lt;p&gt;Instead of computing every face of the drawing, the tracer shoots a ray to the first wall and then hugs it, always taking the first turn clockwise, until it's back where it started. Only geometry the walk actually touches is looked at, so a simple region traces in under a millisecond whether the drawing has 1,000 entities or 100,000. This round it learned to find the wall past whatever stands in the way, to get past fillets that touch a line without ending on it, and to work inside self-crossing splines. Hatches now also export to DXF.&lt;/p&gt;

&lt;h2&gt;
  
  
  Drawing 125,000 entities without melting
&lt;/h2&gt;

&lt;p&gt;Zoomed all the way out on a large drawing, almost every entity is a few pixels across, yet each one was still drawn in full: text laid out letter by letter, dimensions given arrowheads and labels, splines traced point by point.&lt;/p&gt;

&lt;p&gt;Three changes fixed most of it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Sketches.&lt;/strong&gt; Every renderer gained a &lt;code&gt;renderSketch&lt;/code&gt;: what an entity looks like from afar, without the detail that costs. Text becomes a bar over each row of lettering, a dimension its extension lines and a block where the label goes, a hatch its area filled flat, a whole ellipse an octagon. Lines, arcs, circles, polylines and splines are already about as cheap as any sketch of them would be, so they're drawn as they are.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rectangles below 5 px.&lt;/strong&gt; Anything whose bounds are under 5 px on screen is drawn with a single &lt;code&gt;fillRect&lt;/code&gt; in its colour. On a 125,000-entity drawing seen whole, the full path took about &lt;strong&gt;670 ms a frame&lt;/strong&gt;. On their own, 125,000 tiny &lt;code&gt;fillRect&lt;/code&gt;s take about &lt;strong&gt;37 ms&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cached bounds.&lt;/strong&gt; The render loop computed every entity's bounds every frame just to cull what's off screen, and text had to be measured on a canvas to do it. Bounds are now cached per entity and dropped whenever the drawing changes. On 100,000 entities that took bounds from about &lt;strong&gt;70 ms a frame to 2–4 ms&lt;/strong&gt;, and a medium-zoom frame from 84 ms to 20 ms.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Selecting everything also stopped being expensive: past 100 selected entities, grips aren't drawn or hit-tested at all, just the selection colour.&lt;/p&gt;

&lt;h2&gt;
  
  
  More to draw and annotate with
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Arcs&lt;/strong&gt; can be drawn the usual ways: start/centre/end, by angle, by chord length, by radius, by tangent direction, or centre first. When the numbers can't make an arc, the terminal says why.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regular polygons&lt;/strong&gt;, from the centre or from an edge, inscribed or circumscribed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Polar arrays&lt;/strong&gt; with a fill angle, next to the grid array.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dimension styles&lt;/strong&gt;, alongside text styles and leader styles, each with a live preview and per-dimension unit formats. They read from and write back to DXF.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A Measure tool&lt;/strong&gt; that reads what surrounds the cursor as you move: the distance to the nearest geometry in each direction, the length or radius of what it hits, and the angle where two lines meet. Click inside a closed area to get its area and perimeter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Points.&lt;/strong&gt; DXF &lt;code&gt;POINT&lt;/code&gt; entities used to be dropped on import. They're now a real entity you can place, move, snap to and export, with a &lt;strong&gt;Divide&lt;/strong&gt; command on the way that drops points at equal distances along a line, arc, circle, ellipse or polyline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copy and paste between tabs&lt;/strong&gt;, and the app is now installable as a PWA.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  33 languages, and prompts as data
&lt;/h2&gt;

&lt;p&gt;The whole UI is now translated into 33 languages with &lt;a href="https://jsverse.github.io/transloco/" rel="noopener noreferrer"&gt;Transloco&lt;/a&gt;, including the command line, which turned out to be the hard part.&lt;/p&gt;

&lt;p&gt;Commands used to return their prompts as English strings with markers spliced in for the terminal to regex back out. Now a prompt is data: a translation key plus named values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// before: the terminal had to parse this back apart&lt;/span&gt;
&lt;span class="nf"&gt;getPrompt&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`Pick center or enter X,Y: {cursor}`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// after: a key and the values the terminal needs to show what's being typed&lt;/span&gt;
&lt;span class="nf"&gt;getPrompt&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;PromptMessage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;command.circle.pickCenter.orXY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;inputPhase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;inputX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;12&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Command &lt;em&gt;names&lt;/em&gt; are translated too, so you can type &lt;code&gt;Kreis&lt;/code&gt; in German or &lt;code&gt;Коло&lt;/code&gt; in Ukrainian. Matching ignores diacritics. For scripts that need an IME (Hindi, Bengali, Punjabi, Thai, Chinese, Japanese, Korean), typing straight into a raw &lt;code&gt;keydown&lt;/code&gt; command line isn't reliable, so those languages show &lt;code&gt;"Circle 円"&lt;/code&gt; and accept the English word as input.&lt;/p&gt;

&lt;p&gt;Every command also now writes what it did to an expandable terminal history, with coordinates highlighted.&lt;/p&gt;

&lt;h2&gt;
  
  
  What error reporting dug up
&lt;/h2&gt;

&lt;p&gt;I added Sentry with tight defaults: errors only, no session replay, no user info, cookies, headers or console output, each distinct error at most once per session, and source maps uploaded from the production build. Within days it found bugs I would never have hit myself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A blank page for people who block site data.&lt;/strong&gt; When a browser is set to block storage for a site, &lt;code&gt;indexedDB.open&lt;/code&gt; fails with &lt;em&gt;"The user denied permission to access the database."&lt;/em&gt; Two &lt;code&gt;APP_INITIALIZER&lt;/code&gt;s loaded stored fonts and hatch patterns from IndexedDB, and &lt;strong&gt;a rejected initializer aborts Angular's bootstrap&lt;/strong&gt;. Those users got nothing at all. Now they get the app, working in memory, with every storage failure caught and reported instead of fatal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A &lt;code&gt;RangeError&lt;/code&gt; with no recursion in sight.&lt;/strong&gt; &lt;em&gt;"Maximum call stack size exceeded"&lt;/em&gt; came from this line in the snap index:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;pts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spreading an array into a call passes every element as a separate argument, and arguments live on the call stack. In Node, the largest array I could spread into one &lt;code&gt;push()&lt;/code&gt; was &lt;strong&gt;110,153&lt;/strong&gt; elements; doubling the stack size raised the limit to 255,561. A drawing where hundreds of lines cross at one point puts that many intersections under a single coordinate, and moving the mouse near it crashed. A plain loop fixes it, and it turned out to be faster anyway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What people actually open.&lt;/strong&gt; The DXF parser now reports which entity types it couldn't read. By far the most common is block references (&lt;code&gt;INSERT&lt;/code&gt;), followed by &lt;code&gt;3DFACE&lt;/code&gt;, &lt;code&gt;SOLID&lt;/code&gt; and &lt;code&gt;POINT&lt;/code&gt;. That last one is why points exist now, and it set the order of what's next.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Block references are first: every block exploded into its entities on import, with its own move, scale, rotation and mirror applied. Then &lt;code&gt;SOLID&lt;/code&gt; and &lt;code&gt;3DFACE&lt;/code&gt;, which map neatly onto hatches and lines.&lt;/p&gt;

&lt;p&gt;If you want to see where it's at, try it at &lt;strong&gt;&lt;a href="https://kulmanlab.com" rel="noopener noreferrer"&gt;kulmanlab.com&lt;/a&gt;&lt;/strong&gt;. There's no account to create, since there's no server to keep one on; it works best with a mouse and keyboard. If you open a DXF that doesn't come through right, I'd love to hear about it.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>angular</category>
      <category>typescript</category>
      <category>showdev</category>
    </item>
    <item>
      <title>From 0 to 500 DAU with Zero Marketing Budget: 4 Months Building a Web-Based 2D CAD</title>
      <dc:creator>kulman Lab</dc:creator>
      <pubDate>Mon, 31 Aug 2026 15:26:33 +0000</pubDate>
      <link>https://dev.to/kulman_lab_9ff2748a5207d1/from-0-to-500-dau-with-zero-marketing-budget-4-months-building-a-web-based-2d-cad-2al9</link>
      <guid>https://dev.to/kulman_lab_9ff2748a5207d1/from-0-to-500-dau-with-zero-marketing-budget-4-months-building-a-web-based-2d-cad-2al9</guid>
      <description>&lt;p&gt;Hey dev.to community! 👋&lt;/p&gt;

&lt;p&gt;I'm Volodymyr, a Java Engineer with over 12 years of experience in coding and building distributed backend systems. &lt;/p&gt;

&lt;p&gt;For the past 3+ years, alongside my full-time job, I've been building a side project called &lt;strong&gt;&lt;a href="https://kulmanlab.com" rel="noopener noreferrer"&gt;KulmanLab&lt;/a&gt;&lt;/strong&gt;. It is a completely free, browser-based, offline-first 2D CAD editor for technical drawings built with &lt;strong&gt;Angular&lt;/strong&gt;, &lt;strong&gt;TypeScript&lt;/strong&gt;, and &lt;strong&gt;HTML5 Canvas&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A few months ago, I rolled out the core foundation. Today, I want to share an honest, transparent breakdown of what happened next: &lt;strong&gt;the architectural decisions, performance challenges, AI workflows, failed marketing experiments, and real user metrics.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you are a developer looking to push a complex side project into production, building a micro-SaaS, or working with heavy client-side Canvas rendering, this is for you.&lt;/p&gt;




&lt;h2&gt;
  
  
  🏗 The Architecture: Why a Backend Developer Built a Serverless Web App
&lt;/h2&gt;

&lt;p&gt;Here is the irony: Despite 12+ years specializing in Java, Spring, microservices, and databases, I deliberately chose a &lt;strong&gt;100% Client-Side Only / Serverless architecture&lt;/strong&gt; for KulmanLab.&lt;/p&gt;

&lt;p&gt;I know firsthand how much maintenance, infrastructure, and operational effort backend services require. For a side project, I wanted zero server overhead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Architecture Principles:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No Server Calculations:&lt;/strong&gt; All geometry calculations, 2D vector pathing, state management, and rendering happen strictly in the browser.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Offline-First Storage:&lt;/strong&gt; User project files and drawing history persist locally via &lt;strong&gt;IndexedDB&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero Onboarding Friction:&lt;/strong&gt; No registration, no email verification, no trial paywalls. Users open the app and instantly start drawing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interoperability:&lt;/strong&gt; Native import/export for CAD standards, including &lt;code&gt;.dxf&lt;/code&gt; format support.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🛠 Features &amp;amp; Engineering Milestones
&lt;/h2&gt;

&lt;p&gt;In the last four months alone, the project evolved from a basic line renderer to a capable technical drawing tool:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Rich Text Engine (&lt;code&gt;MTEXT&lt;/code&gt; / &lt;code&gt;TEXT&lt;/code&gt;):&lt;/strong&gt; Handled parsing and rendering formatted CAD typography natively onto the HTML5 Canvas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vector Hatching Patterns (&lt;code&gt;HATCH&lt;/code&gt;):&lt;/strong&gt; Generates and fills complex closed boundaries with repeating vector patterns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Dimension Lines:&lt;/strong&gt; Auto-calculates linear, angular, and radial dimensions with real-time vector alignment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layouts &amp;amp; Paper Space:&lt;/strong&gt; Printing setup, view scaling, and readying blueprints for export.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI-Assisted Iteration:&lt;/strong&gt; Using tools like Claude Code accelerated feature shipping by up to 10x, though much of the day-to-day still comes down to deep code inspection, edge-case fixes, and Canvas performance optimization.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  📊 Real Growth Metrics (No Fluff)
&lt;/h2&gt;

&lt;p&gt;Initial user traction started in early May, but organic growth accelerated significantly through the summer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Monthly Active Users (MAU):&lt;/strong&gt; ~7,000 unique users / 30 days&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Peak Daily Active Users (DAU):&lt;/strong&gt; &lt;strong&gt;520 DAU&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Average Session Duration:&lt;/strong&gt; &lt;strong&gt;13–15 minutes&lt;/strong&gt; (A critical engagement metric showing users actually complete technical drawings rather than bouncing).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traffic Quality:&lt;/strong&gt; 100% organic without paid ads.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key Traffic Insights:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Organic Search (SEO):&lt;/strong&gt; Moving the web application from the main domain root to &lt;code&gt;app.kulmanlab.com&lt;/code&gt; and building a dedicated static landing page on &lt;code&gt;kulmanlab.com&lt;/code&gt; provided the single biggest SEO boost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Global Distribution:&lt;/strong&gt; Broad global usage with steady engagement across Europe, North America, and Asia.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OS &amp;amp; Browsers:&lt;/strong&gt; Heavily dominated by desktop Chrome and Edge users, fitting the B2B/engineering user persona.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb955m4jgj7t9dgex4hm8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb955m4jgj7t9dgex4hm8.png" alt=" " width="800" height="361"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  📣 Marketing Experiments: What Worked vs. What Failed
&lt;/h2&gt;

&lt;p&gt;As a solo engineer without a marketing budget, getting the word out was pure experimentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ What Didn't Work
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Engineering &amp;amp; CNC Forums:&lt;/strong&gt; Low reach, high risk of instant bans for self-promotion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cold Pitching Reviewers/Bloggers:&lt;/strong&gt; Near 0% response rate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Product Hunt:&lt;/strong&gt; "Unfeatured" launch yielded virtually zero traffic or SEO indexation benefit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Student Discord Servers &amp;amp; Social Media (X/Facebook):&lt;/strong&gt; High time investment with minimal conversion for a specialized B2B tool.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ✅ What Actually Worked
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;SEO Optimization &amp;amp; Landing Page Separation:&lt;/strong&gt; Separating the landing marketing site from the functional app (&lt;code&gt;app.kulmanlab.com&lt;/code&gt;) drastically improved Google indexing for CAD queries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Building in Public &amp;amp; Developer Communities:&lt;/strong&gt; Sharing genuine technical challenges, changelogs, and honest post-mortems on developer platforms created authentic organic word-of-mouth.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  💡 Key Takeaways for Developers
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Embrace Constraints:&lt;/strong&gt; Eliminating the backend forced me to solve complex state and rendering logic on the client side, keeping infrastructure costs at $0 while delivering instant speed to users.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus on Time-to-Value:&lt;/strong&gt; Removing logins, sign-ups, and paywalls created frictionless trial loops.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Listen to Local Storage:&lt;/strong&gt; Using &lt;code&gt;IndexedDB&lt;/code&gt; enabled an offline-first experience that users in low-connectivity environments deeply appreciate.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Try it out &amp;amp; feedback welcome!
&lt;/h3&gt;

&lt;p&gt;You can test the app directly in your browser without creating an account:&lt;br&gt;
🌐 &lt;strong&gt;Web App:&lt;/strong&gt; &lt;a href="https://kulmanlab.com" rel="noopener noreferrer"&gt;kulmanlab.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'd love to hear your thoughts on building complex Canvas-based applications in Angular! How do you handle heavy client-side state and rendering performance in your own projects? Let's discuss in the comments below! 👇&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>showdev</category>
      <category>angular</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Product Update: Vector Pins, Measurement Overhauls, and ALIGN Polish</title>
      <dc:creator>kulman Lab</dc:creator>
      <pubDate>Sat, 11 Jul 2026 05:41:48 +0000</pubDate>
      <link>https://dev.to/kulman_lab_9ff2748a5207d1/product-update-vector-pins-measurement-overhauls-and-align-polish-25ha</link>
      <guid>https://dev.to/kulman_lab_9ff2748a5207d1/product-update-vector-pins-measurement-overhauls-and-align-polish-25ha</guid>
      <description>&lt;p&gt;Hey everyone! We’ve been heads-down refining our canvas experience following the &lt;strong&gt;ALIGN&lt;/strong&gt; milestone (July 8–10, 2026). This update brings highly requested &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foomb8j4m70221vmk017v.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foomb8j4m70221vmk017v.gif" alt=" " width="600" height="343"&gt;&lt;/a&gt;&lt;br&gt;
drafting precision, UX quality-of-life improvements, and a handful of crucial bug fixes. &lt;/p&gt;

&lt;p&gt;Let’s dive into what’s new! 👇&lt;/p&gt;




&lt;h2&gt;
  
  
  ✨ New Feature: Vector Pins (Object Snap Tracking)
&lt;/h2&gt;

&lt;p&gt;We are incredibly excited to introduce &lt;strong&gt;Vector Pins&lt;/strong&gt;, bringing powerful tracking points to your drafting workflow. Refined across 10 precise commits, this feature dramatically improves alignment accuracy without needing extra construction lines.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;How it works:&lt;/strong&gt; Hover over any grip point for &lt;strong&gt;500ms&lt;/strong&gt; to "pin" it. Each active pin projects dynamic horizontal and vertical reference lines that your cursor automatically snaps onto, complete with dashed guides and accent markers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Angle-Locked Rays:&lt;/strong&gt; Ortho and polar inputs now intelligently snap to intersections with pin reference lines. This is fully supported across line drawing, circle radii, arc picks, and polylines (which we ported onto &lt;code&gt;DirectionalInputCommand&lt;/code&gt; to make this possible!).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistent Lifecycle:&lt;/strong&gt; Pins survive across sequential actions so you can use them seamlessly with &lt;code&gt;MOVE&lt;/code&gt; or &lt;code&gt;COPY&lt;/code&gt;. However, to keep your canvas clean, they are automatically cleared when a brand-new command fires or when the active command finishes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Under the Hood 🛠️:&lt;/strong&gt; To keep our codebase maintainable, we decoupled the pin logic from &lt;code&gt;CanvasComponent&lt;/code&gt; into a dedicated &lt;code&gt;VectorPinsService&lt;/code&gt;. We also centralized the angle-lock anchor capture inside &lt;code&gt;updateAngleLock&lt;/code&gt;, eliminating redundant per-command overrides.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📏 Revamped Measurement Tools
&lt;/h2&gt;

&lt;p&gt;We’ve overhauled the &lt;code&gt;DISTANCE&lt;/code&gt; and &lt;code&gt;ANGLE&lt;/code&gt; tools to make taking quick canvas metrics much less frustrating.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sticky Results:&lt;/strong&gt; Measurement overlays now stay on your screen until you explicitly hit &lt;code&gt;Escape&lt;/code&gt;, rather than vanishing the moment you move your mouse. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Endpoint Grips:&lt;/strong&gt; The &lt;code&gt;DISTANCE&lt;/code&gt; tool now displays interactive grips at both endpoints for better visual context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved UI States:&lt;/strong&gt; Both tools now leverage your system's accent color—rendering as &lt;strong&gt;dashed lines&lt;/strong&gt; while actively picking, and turning &lt;strong&gt;solid&lt;/strong&gt; once the measurement is locked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control Bar:&lt;/strong&gt; Polished the &lt;code&gt;ANGL&lt;/code&gt; display component in the control bar for better readability.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧭 ALIGN Polish &amp;amp; UI Refinements
&lt;/h2&gt;

&lt;p&gt;Following the initial rollout of the &lt;code&gt;ALIGN&lt;/code&gt; command, we spent time ironing out the UX kinks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Copy Consistency:&lt;/strong&gt; Updated all prompt text to match our standard command vocabulary (e.g., using "pick" uniformly).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Icon Redesign:&lt;/strong&gt; The Align toolbar icon underwent a beautiful 7-commit redesign. It now features clean grip-to-grip dashed connections, a perfectly vertical solid right boundary line, and a flipped layout to maximize visual clarity. Its dashed-line width now perfectly matches the Offset icon.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layout Organization:&lt;/strong&gt; Swapped the positions of &lt;strong&gt;Align&lt;/strong&gt; and &lt;strong&gt;Fillet&lt;/strong&gt; in the edit panel for a more intuitive tool grouping.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🐛 Fixes &amp;amp; Visual Tweaks
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Viewport Isolation:&lt;/strong&gt; Fixed a frustrating bug where model-space entities could accidentally be selected &lt;em&gt;through&lt;/em&gt; layout viewports. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Color Standardization:&lt;/strong&gt; Intersection &lt;code&gt;X&lt;/code&gt; markers are now drawn in the system accent color instead of green, keeping our UI palette unified.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;What do you think of the new Vector Pins? Let us know in the comments below or drop your feature requests!&lt;/em&gt; 🚀&lt;br&gt;
&lt;a href="https://kulmanlab.com" rel="noopener noreferrer"&gt;https://kulmanlab.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>changelog</category>
      <category>webdev</category>
      <category>cad</category>
      <category>engineering</category>
    </item>
    <item>
      <title>Overhauling a CAD Engine: Rebuilding Multileader Text Layout &amp; Editor Mechanics</title>
      <dc:creator>kulman Lab</dc:creator>
      <pubDate>Thu, 18 Jun 2026 07:15:50 +0000</pubDate>
      <link>https://dev.to/kulman_lab_9ff2748a5207d1/overhauling-a-cad-engine-rebuilding-multileader-text-layout-editor-mechanics-139j</link>
      <guid>https://dev.to/kulman_lab_9ff2748a5207d1/overhauling-a-cad-engine-rebuilding-multileader-text-layout-editor-mechanics-139j</guid>
      <description>&lt;p&gt;Hey everyone! 👋 &lt;/p&gt;

&lt;p&gt;I just shipped a massive architectural overhaul to our &lt;strong&gt;Multileader (MLEADER)&lt;/strong&gt; graphics engine alongside some long-overdue quality-of-life updates to our canvas &lt;strong&gt;Text Editor&lt;/strong&gt;[cite: 1]. Here is a quick look at what changed, why I changed it, and how I solved it[cite: 1].&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠 1. Moving to Structured Rich Text Data (&lt;code&gt;TextSegment[]&lt;/code&gt;)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;Previously, multileaders treated text as a single string block[cite: 1]. This made formatting highly rigid and prone to failure when dealing with native DXF inline codes (&lt;code&gt;\H&lt;/code&gt;, &lt;code&gt;\f&lt;/code&gt;, &lt;code&gt;\L&lt;/code&gt;, etc.)[cite: 1]. Worse yet, a legacy layout constraint meant that &lt;strong&gt;only the first line of text would actually render&lt;/strong&gt;[cite: 1].&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;I completely decoupled text storage from raw string fields[cite: 1]. Multileader content is now processed into an array of typed segments (&lt;code&gt;TextSegment[]&lt;/code&gt;), bringing true multi-line and per-character rich text formatting support[cite: 1]. I also isolated the recursive parsing logic into a unified utility: &lt;code&gt;text-parser-utils.ts&lt;/code&gt;[cite: 1].&lt;/p&gt;




&lt;h2&gt;
  
  
  🎨 2. Solving Variable Row Heights &amp;amp; Text Overlaps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;When text strings contain varying font sizes or special character drops (like &lt;code&gt;g&lt;/code&gt;, &lt;code&gt;j&lt;/code&gt;, &lt;code&gt;p&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;), standard static height offsets cause lines to overlap, clip, or look completely crushed[cite: 1]. &lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;I migrated from a static "entity-wide" height constant to a &lt;strong&gt;dynamic per-row ribbon architecture&lt;/strong&gt;[cite: 1]:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;First-Row Scaling:&lt;/strong&gt; The attachment offset is now dynamically scaled using the maximum character height of the first row only, preventing inconsistent alignment[cite: 1].&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per-Row Ribbon Bounds:&lt;/strong&gt; Each row computes its own bounding box on layout passes[cite: 1]. Mixed font sizes on a single line will never bleed into adjacent rows[cite: 1].&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coordinate Tree Alignment:&lt;/strong&gt; I aligned the rendering coordinate space to mirror our primary canvas &lt;code&gt;Text&lt;/code&gt; entity[cite: 1]. This explicitly resolves selection box overflows for unusually tall or accented characters[cite: 1].&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Line-Weight Inheritance:&lt;/strong&gt; Underline thickness now dynamically inherits its thickness from the adjacent leader line weight[cite: 1].&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🖱 3. Inline Canvas Text Editor Integration
&lt;/h2&gt;

&lt;p&gt;Building a modern CAD experience means getting rid of modal popups or manual text prompt windows[cite: 1]. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Double-Click-To-Edit:&lt;/strong&gt; Users can now double-click a multileader to activate an in-place text editor directly on the canvas with real-time cursor tracking, character selection, and accurate hit-testing[cite: 1].&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Creation Flow Hooks:&lt;/strong&gt; The moment a user places the final point of a new leader, the application automatically kicks into the inline text editor state right at the text-placement position[cite: 1].&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layout Anchoring:&lt;/strong&gt; The editor popup accurately anchors to the stub side of the leader[cite: 1]. Frame-resize handles (grips) allow word-wrap behaviors to scale exactly like standard canvas text bodies[cite: 1].&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ⚡ 4. General UX &amp;amp; Framework Refinements
&lt;/h2&gt;

&lt;p&gt;I polished out some of the most annoying workflow friction points inside the core workspace:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Command Optimization:&lt;/strong&gt; Commands like &lt;code&gt;Leader+&lt;/code&gt; or &lt;code&gt;Leader-&lt;/code&gt; now check your active canvas selection first[cite: 1]. If a valid multileader is highlighted, it bypasses the manual item selection step[cite: 1].&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Canvas Focus Locks:&lt;/strong&gt; Pressing &lt;code&gt;Enter&lt;/code&gt; while adjusting the text height input box immediately returns viewport focus back to the canvas context[cite: 1].&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Ghost Height Bug:&lt;/strong&gt; Fixed a silent bug where modifying the font size on specific multileader elements would run fine but result in a no-op due to a missing internal text height setter[cite: 1].&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean Closures:&lt;/strong&gt; Clicking outside the boundaries of an active text editor gracefully closes out and saves the current state without accidentally registering a brand new selection window on your canvas[cite: 1].&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/VKWyncl8bZI"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>typescript</category>
      <category>graphics</category>
      <category>cad</category>
    </item>
    <item>
      <title>Text Editor is live!</title>
      <dc:creator>kulman Lab</dc:creator>
      <pubDate>Wed, 10 Jun 2026 13:40:28 +0000</pubDate>
      <link>https://dev.to/kulman_lab_9ff2748a5207d1/text-editor-is-live-5ge2</link>
      <guid>https://dev.to/kulman_lab_9ff2748a5207d1/text-editor-is-live-5ge2</guid>
      <description>&lt;h1&gt;
  
  
  Building a DXF-Compatible Rich Text Editor for a Web-Based CAD Tool
&lt;/h1&gt;

&lt;p&gt;Last week I spent most of my time working on the text editor for my web-based CAD application, &lt;a href="https://kulmanlab.com/" rel="noopener noreferrer"&gt;https://kulmanlab.com/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This update was focused on improving DXF compatibility and making text editing more flexible, predictable, and closer to how modern design tools behave.&lt;/p&gt;




&lt;h2&gt;
  
  
  📌 DXF MText Support
&lt;/h2&gt;

&lt;p&gt;First of all, &lt;strong&gt;MText from DXF is now fully supported&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This means you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create your blueprint in any CAD tool of your choice&lt;/li&gt;
&lt;li&gt;Export it as DXF&lt;/li&gt;
&lt;li&gt;Import it into KulmanLab&lt;/li&gt;
&lt;li&gt;And keep text styling preserved&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This was an important milestone because DXF text handling is often inconsistent between tools. Preserving formatting improves interoperability and reduces manual rework.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔄 Unifying Text: One Object Instead of Two
&lt;/h2&gt;

&lt;p&gt;Previously, the system had two separate concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Text&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MText&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I decided to simplify this.&lt;/p&gt;

&lt;p&gt;Now everything is unified into a &lt;strong&gt;single Text object&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why?
&lt;/h3&gt;

&lt;p&gt;A "simple" text entity is essentially just a multiline text object with one line. Maintaining two separate implementations added complexity without much real benefit.&lt;/p&gt;

&lt;p&gt;Unifying them makes the system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easier to maintain&lt;/li&gt;
&lt;li&gt;More predictable&lt;/li&gt;
&lt;li&gt;More extensible for future formatting features&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ✍️ Rich Text Editing Experience
&lt;/h2&gt;

&lt;p&gt;The Text command now supports a more modern editing experience.&lt;/p&gt;

&lt;p&gt;When placing text, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Choose font family&lt;/li&gt;
&lt;li&gt;Set font size&lt;/li&gt;
&lt;li&gt;Apply styles:

&lt;ul&gt;
&lt;li&gt;Bold&lt;/li&gt;
&lt;li&gt;Italic&lt;/li&gt;
&lt;li&gt;Strikethrough (more coming soon)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After placement, the text is rendered inside a &lt;strong&gt;blue bounding frame&lt;/strong&gt;, which defines the wrapping area.&lt;/p&gt;

&lt;h3&gt;
  
  
  Interactive layout control
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A live resize handle allows you to adjust wrapping width dynamically&lt;/li&gt;
&lt;li&gt;Text reflows instantly as you resize the frame&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes layout control much closer to what you'd expect from modern design tools rather than traditional CAD text input.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 Smart Cursor &amp;amp; Formatting Behavior
&lt;/h2&gt;

&lt;p&gt;One of the trickier parts was making the cursor behave correctly in a mixed-format text environment.&lt;/p&gt;

&lt;p&gt;Now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cursor height adapts to the active character style&lt;/li&gt;
&lt;li&gt;Cursor position reflects font size changes in real time&lt;/li&gt;
&lt;li&gt;Mixed formatting doesn’t break alignment or insertion behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensures consistent editing even when different styles are applied within the same text block.&lt;/p&gt;




&lt;h2&gt;
  
  
  📍 Multiple Attachment Points
&lt;/h2&gt;

&lt;p&gt;Text objects now support multiple attachment anchors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Top-left&lt;/li&gt;
&lt;li&gt;Top-right&lt;/li&gt;
&lt;li&gt;Bottom-left&lt;/li&gt;
&lt;li&gt;Bottom-right&lt;/li&gt;
&lt;li&gt;Center (and others)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes positioning more flexible, especially in technical drawings where alignment matters.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎨 Character-Level Styling
&lt;/h2&gt;

&lt;p&gt;One of the more powerful features is &lt;strong&gt;fine-grained text styling&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bold individual words&lt;/li&gt;
&lt;li&gt;Change font size per character&lt;/li&gt;
&lt;li&gt;Mix fonts within a single text object&lt;/li&gt;
&lt;li&gt;Combine multiple styles in one sentence&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This moves the editor closer to a true rich-text system rather than a simple label tool.&lt;/p&gt;




&lt;h2&gt;
  
  
  📦 Frame Visibility &amp;amp; Layout Control
&lt;/h2&gt;

&lt;p&gt;A frame toggle allows you to show or hide the bounding box around text.&lt;/p&gt;

&lt;p&gt;This is useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Layout debugging&lt;/li&gt;
&lt;li&gt;Precise alignment&lt;/li&gt;
&lt;li&gt;Clean final rendering&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🚀 What’s Next
&lt;/h2&gt;

&lt;p&gt;I believe the application is now usable enough for most basic workflows.&lt;/p&gt;

&lt;p&gt;Next step is shifting focus toward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SEO optimization&lt;/li&gt;
&lt;li&gt;Landing page improvements&lt;/li&gt;
&lt;li&gt;Product promotion and visibility&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The core editing engine is becoming stable enough to support this transition.&lt;/p&gt;




&lt;h2&gt;
  
  
  💭 Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;Building a text system that behaves consistently across CAD + web constraints turned out to be more complex than expected.&lt;/p&gt;

&lt;p&gt;But the result is a much more flexible foundation that should support future features without major rewrites.&lt;/p&gt;

&lt;p&gt;If you're interested in web-based CAD or DXF tooling, feel free to check out:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://kulmanlab.com/" rel="noopener noreferrer"&gt;https://kulmanlab.com/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>showdev</category>
      <category>software</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Building an Adaptive Grid &amp; Smart Snapping System for Web CAD</title>
      <dc:creator>kulman Lab</dc:creator>
      <pubDate>Tue, 02 Jun 2026 07:04:21 +0000</pubDate>
      <link>https://dev.to/kulman_lab_9ff2748a5207d1/building-an-adaptive-grid-smart-snapping-system-for-web-cad-4o17</link>
      <guid>https://dev.to/kulman_lab_9ff2748a5207d1/building-an-adaptive-grid-smart-snapping-system-for-web-cad-4o17</guid>
      <description>&lt;p&gt;Hey dev community! 👋&lt;/p&gt;

&lt;p&gt;I just rolled out a major feature update to our canvas-based editor: &lt;strong&gt;Grid &amp;amp; Snap&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Implementing a basic grid is easy, but making it feel intuitive, performant, and "smart" at any zoom scale requires a bit of math and careful UX prioritization. Here is a breakdown of how I tackled it.&lt;/p&gt;




&lt;h2&gt;
  
  
  📐 The Adaptive Grid
&lt;/h2&gt;

&lt;p&gt;A major pain point with standard canvas grids is clutter. If you zoom out, the grid lines choke the screen; if you zoom in, they disappear. &lt;/p&gt;

&lt;p&gt;To solve this, our new &lt;strong&gt;Grid&lt;/strong&gt; toggle overlays dots or lines that &lt;strong&gt;adapt automatically as you zoom&lt;/strong&gt;. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Math:&lt;/strong&gt; It dynamically scales to land on clean, round values (such as $1, 2, 5, 10$… at any power of ten). &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Result:&lt;/strong&gt; The grid stays perfectly readable and uncluttered whether a user is looking at a macro layout or zooming into micro-details.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧲 Smart Snapping (With a Hierarchy)
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Snap&lt;/strong&gt; toggle locks the cursor to the nearest grid point during drawing or editing. However, blindly snapping to a grid can ruin precision when working near existing lines. &lt;/p&gt;

&lt;p&gt;To fix this, I implemented a priority hierarchy:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Geometry always wins.&lt;/strong&gt; Grid snapping &lt;em&gt;only&lt;/em&gt; kicks in when the cursor is not already near an endpoint or intersection. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Additionally, I decoupled the features: the two toggles work independently (you can snap to an invisible grid if you prefer a clean UI), and user settings persist across sessions.&lt;/p&gt;

&lt;h2&gt;
  
  
  📄 Context-Aware Scaling
&lt;/h2&gt;

&lt;p&gt;A grid shouldn't behave the same way in a design workspace as it does on a printable sheet. The system intelligently detects the active workspace mode:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Workspace Mode&lt;/th&gt;
&lt;th&gt;Grid Behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Layout Mode&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Clipped cleanly to the designated paper boundary area.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Inside Viewports&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dynamics shift to seamlessly follow the model's coordinate scale.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  How would you approach this?
&lt;/h3&gt;

&lt;p&gt;If you’ve built something similar, how did you handle performance when rendering thousands of grid points on a high-DPI canvas? &lt;/p&gt;

&lt;p&gt;I'd love to hear your thoughts or answer any questions about the math behind the adaptive scaling! 👇&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>canvas</category>
      <category>geometry</category>
    </item>
    <item>
      <title>Building a Web CAD: How We Implemented Precise X,Y Coordinate Input</title>
      <dc:creator>kulman Lab</dc:creator>
      <pubDate>Mon, 01 Jun 2026 08:26:22 +0000</pubDate>
      <link>https://dev.to/kulman_lab_9ff2748a5207d1/building-a-web-cad-how-we-implemented-precise-xy-coordinate-input-2i55</link>
      <guid>https://dev.to/kulman_lab_9ff2748a5207d1/building-a-web-cad-how-we-implemented-precise-xy-coordinate-input-2i55</guid>
      <description>&lt;p&gt;Hey dev community! 👋&lt;/p&gt;

&lt;p&gt;We’ve been working hard on kulmanlab.com, a browser-based CAD and drafting tool. One of the biggest challenges in building web-based design tools is matching the precision and keyboard-driven UX that engineers and designers expect from desktop software.&lt;/p&gt;

&lt;p&gt;Today, we rolled out a major feature that addresses exactly that: Precise X,Y Coordinate Input.&lt;/p&gt;

&lt;p&gt;Here is a look at how we designed the user experience, the logic behind the terminal inputs, and what commands now support it.&lt;/p&gt;

&lt;p&gt;🧠 The UX Design: Fast, Keyboard-First Input&lt;br&gt;
When you are deep in a drafting workflow, taking your hands off the keyboard to aim with a mouse ruins your momentum. We wanted a seamless transition between mouse-clicking and typing.&lt;/p&gt;

&lt;p&gt;Now, whenever a command asks the user to "pick a point," the terminal prompt dynamically updates to display or enter X,Y.&lt;/p&gt;

&lt;p&gt;Here is the exact state machine we implemented for the input flow:&lt;/p&gt;

&lt;p&gt;X-Value Entry: The user types digits, decimals (.), or a leading negative sign (-).&lt;/p&gt;

&lt;p&gt;The Switch: Pressing , (comma) or Space locks the X value and shifts focus to Y. The terminal UI updates to show [X], [cursor].&lt;/p&gt;

&lt;p&gt;Commit: The user types the Y value and hits Enter or Space to instantly plot the point.&lt;/p&gt;

&lt;p&gt;Smart Backspacing: Pressing backspace deletes the last digit. If the Y field is completely empty, hitting backspace automatically jumps the user back to editing the X value.&lt;/p&gt;

&lt;p&gt;Graceful Cancellation: Pressing Escape clears the coordinate input buffer without breaking or canceling the active tool/command.&lt;/p&gt;

&lt;p&gt;🛠️ Scaling It Across the Architecture&lt;br&gt;
Instead of hardcoding this input logic into every single tool, we hooked it directly into our global point-snapping and input-resolution engine. Because of this architectural choice, we were able to instantly scale coordinate entry across almost every tool in the app:&lt;/p&gt;

&lt;p&gt;Creation Tools: Line, Polyline, Arc, Circle, Ellipse, Rectangle, Spline (fit &amp;amp; CV), Text&lt;/p&gt;

&lt;p&gt;Transformations: Move, Copy, Rotate, Scale, Mirror&lt;/p&gt;

&lt;p&gt;Dimensions &amp;amp; Math: Leader, Dimension (linear, aligned, continue), Distance, Angle&lt;/p&gt;

&lt;p&gt;Viewports: Rectangle &amp;amp; Copy viewports&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>showdev</category>
      <category>ux</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Changelog: Streamlined Annotations, Sticky Commands, and UI Polish</title>
      <dc:creator>kulman Lab</dc:creator>
      <pubDate>Sun, 31 May 2026 14:26:25 +0000</pubDate>
      <link>https://dev.to/kulman_lab_9ff2748a5207d1/changelog-streamlined-annotations-sticky-commands-and-ui-polish-3ah8</link>
      <guid>https://dev.to/kulman_lab_9ff2748a5207d1/changelog-streamlined-annotations-sticky-commands-and-ui-polish-3ah8</guid>
      <description>&lt;p&gt;Hey everyone! We've been hard at work refining the workspace, fixing nagging bugs, and smoothing out user workflows. Here is a comprehensive breakdown of everything that went into the latest release. Check it out &lt;a href="https://kulmanlab.com/" rel="noopener noreferrer"&gt;https://kulmanlab.com/&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🎨 Annotation &amp;amp; Dimensions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Consistent Defaults
&lt;/h3&gt;

&lt;p&gt;To keep your drawings looking uniform without manual tweaking, all dimensions and leaders now use standardized defaults out of the box:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Text height / Arrow size:&lt;/strong&gt; 12&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Text/landing gap:&lt;/strong&gt; 1&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extension line offset/extension:&lt;/strong&gt; 5&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Fixes &amp;amp; Behavior
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Toolbar Property Inheritance:&lt;/strong&gt; Dimensions (linear, aligned, angular, radius, diameter) and leaders now correctly inherit &lt;strong&gt;color&lt;/strong&gt;, &lt;strong&gt;linetype&lt;/strong&gt;, and &lt;strong&gt;lineweight&lt;/strong&gt; directly from the toolbar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Under-the-Hood Cleanup:&lt;/strong&gt; Leaders now extend &lt;code&gt;LinearEntity&lt;/code&gt; instead of duplicating fields, keeping our codebase leaner.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Match Properties:&lt;/strong&gt; This will no longer overwrite your text content (labels/custom text)—only style properties are transferred now.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Arc Command:&lt;/strong&gt; Now correctly picks up lineweight from the toolbar.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ↩️ Leader Editing Improvements
&lt;/h2&gt;

&lt;p&gt;Working with leaders just got a lot more intuitive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Text Input Navigation:&lt;/strong&gt; Supports full cursor navigation using &lt;code&gt;←&lt;/code&gt; and &lt;code&gt;→&lt;/code&gt; to move through text, and &lt;code&gt;Delete&lt;/code&gt; to remove characters at the cursor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terminal Prompts:&lt;/strong&gt; The locked length input is now clearly highlighted in the terminal prompt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leader+ (Chain Mode):&lt;/strong&gt; Add multiple arms sequentially without having to restart the command every single time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leader− (Chain Mode):&lt;/strong&gt; Remove multiple arms sequentially; simply hit &lt;code&gt;Enter&lt;/code&gt; or &lt;code&gt;Space&lt;/code&gt; to finish.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📋 Productivity &amp;amp; Copy Commands
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is "Sticky Mode"?&lt;/strong&gt; &lt;br&gt;
Instead of dropping a single object and ending the command, sticky mode keeps the tool active so you can stamp down multiple copies seamlessly.&lt;/p&gt;

&lt;h3&gt;
  
  
  ViewportCopy
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sticky Mode:&lt;/strong&gt; Stays active after each copy so you can place multiple viewports consecutively.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Precise Placement:&lt;/strong&gt; While angle-locked, you can now type a specific distance for exact placement.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Copy Command
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sticky Mode:&lt;/strong&gt; Stays active after each copy until you manually exit by pressing &lt;code&gt;Enter&lt;/code&gt; or &lt;code&gt;Escape&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📐 Fillet &amp;amp; Chamfer
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fillet Persistence:&lt;/strong&gt; The fillet radius now persists between uses and comes pre-selected so typing immediately replaces it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chamfer Persistence:&lt;/strong&gt; Chamfer distances persist, and typing instantly replaces the pre-selected value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Polyline Support:&lt;/strong&gt; Chamfer now works perfectly on polyline corners, and the broken undo functionality has been fully resolved.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🖥️ UI Polish &amp;amp; Enhancements
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Model Space &amp;amp; Navigation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Origin Marker:&lt;/strong&gt; Added at &lt;code&gt;(0,0)&lt;/code&gt; in model space, featuring a vibrant accent color and $2\times$ size for better visibility.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Feedback:&lt;/strong&gt; A dynamic angle readout is now shown in the terminal while you are picking your second point.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layout Tabs:&lt;/strong&gt; The active tab is now taller for better visual hierarchy, right-clicking opens a context menu, and navigation arrows move your selection.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Terminal &amp;amp; Typography
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Terminal UI:&lt;/strong&gt; Refined with a lighter history border, a full-white placeholder, and a panel-matched border.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Typography:&lt;/strong&gt; Replaced the old monospace fonts with a clean &lt;strong&gt;Arial sans-serif&lt;/strong&gt; across the entire application for improved readability.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Dialogs &amp;amp; Widgets
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Page &amp;amp; Print Managers:&lt;/strong&gt; Improved label sizes, border radiuses, and overall layout optimization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layer &amp;amp; Style Widgets:&lt;/strong&gt; Updated with refined border radiuses and full-width fill to match modern UI standards.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;What do you think of these workflow changes? Are there any specific commands you'd like to see get the "sticky" treatment next? Let us know in the comments below!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>news</category>
      <category>showdev</category>
      <category>software</category>
      <category>ui</category>
    </item>
    <item>
      <title>Building a Browser-Based 2d AutoCAD web app with Angular (And the Nightmare of DXF)</title>
      <dc:creator>kulman Lab</dc:creator>
      <pubDate>Fri, 29 May 2026 13:21:47 +0000</pubDate>
      <link>https://dev.to/kulman_lab_9ff2748a5207d1/building-a-browser-based-2d-autocad-web-app-with-angular-and-the-nightmare-of-dxf-2kk8</link>
      <guid>https://dev.to/kulman_lab_9ff2748a5207d1/building-a-browser-based-2d-autocad-web-app-with-angular-and-the-nightmare-of-dxf-2kk8</guid>
      <description>&lt;p&gt;Hey everyone!&lt;/p&gt;

&lt;p&gt;My name is Volodymyr, and I’m one of those developers who didn’t just switch careers—I converted my entire past experience into code. Today, I work as a Java Backend Developer. But my degree says "Civil Engineer," and the years I spent hunching over drawings on traditional drafting tables (&lt;em&gt;kulmans&lt;/em&gt;) and in AutoCAD eventually sparked my own passion project: &lt;a href="https://kulmanlab.com/" rel="noopener noreferrer"&gt;Kulman&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  From Civil Engineering to a DIY CNC Machine
&lt;/h3&gt;

&lt;p&gt;My journey into IT started when I realized that civil engineering was becoming an endless routine of repetitive tasks that carried a massive amount of liability. And the financial compensation? Well, that's a whole different story. During my final year of university, I started learning Java, and about a year later, I landed my first job as a backend developer.&lt;/p&gt;

&lt;p&gt;But that engineering itch never really went away. I fell down the rabbit hole of CNC machines and even ended up building my own custom laser cutter from scratch based on a Ruida controller. I went all-in with a bit of maximalism: I chose a high-power CO₂ laser tube, which meant the entire machine turned out to be quite massive.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk3hpzttrgirz1kvktto6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk3hpzttrgirz1kvktto6.jpg" alt=" " width="799" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fse21katp7h5awou9do15.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fse21katp7h5awou9do15.jpg" alt=" " width="799" height="450"&gt;&lt;/a&gt;&lt;br&gt;
Anyone who has ever dealt with laser cutting or CNC milling knows the struggle: the path from an idea to a finished product often forces you through "monstrous" software. You either have to shell out for an expensive AutoCAD or SolidWorks license, or suffer through free alternatives with terrible UX. My goal was to build a tool that opens in a single second, requires absolutely no registration, and lets you prepare a file for your machine directly in the browser.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project Philosophy: The Power of Static Pages and IndexedDB
&lt;/h3&gt;

&lt;p&gt;The core principle behind KulmanLab is maximum autonomy. The project is hosted on Firebase and runs purely as a static site. &lt;/p&gt;

&lt;p&gt;Why skip the backend?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy-First:&lt;/strong&gt; Your drawings belong to you. They are never saved on a server or transmitted over the network. All processing happens locally on your machine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero Maintenance Costs:&lt;/strong&gt; Eliminating server-side logic means the project can exist entirely without annoying ad banners or paid subscriptions. My only ongoing cost is the domain name.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Offline-First:&lt;/strong&gt; Using IndexedDB, I built a reliable autosave system. Even if you accidentally close your browser or experience a sudden power outage, your files and full edit history remain completely safe in your local browser storage. It’s essentially your own personal, local "cloud." The moment you open a new DXF file or start a project from scratch, everything loads instantly, and your previous work is always right there waiting for you.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To keep the UX familiar and efficient, I chose a classic AutoCAD-style interface: commands can be triggered directly from the keyboard, with prompts and inputs guided by a terminal at the bottom of the screen. While this command-line workflow might have a slight learning curve for total beginners, it drastically speeds up production for anyone used to drafting, as you don't have to constantly hunt for the right button on a toolbar.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technical Challenges: Geometry and Performance
&lt;/h3&gt;

&lt;p&gt;Three years ago, when I first started this journey, I became fascinated by applied geometry problems—things like finding the intersection point between two lines, or a line and an ellipse. I found myself diving deep into the mathematical functions of geometric shapes, deriving systems of equations, and translating them into code. The ability to immediately see the visual results of my math on the frontend was incredibly motivating. It was exactly the kind of instant gratification I felt was missing in backend development.&lt;/p&gt;

&lt;p&gt;For development, I chose a combo of &lt;strong&gt;HTML5 Canvas + Angular&lt;/strong&gt;. Why Angular? To be honest, I was completely disconnected from the mainstream frontend ecosystem at the time. With my backend background, TypeScript felt visually close to Java, and Angular’s structured component approach made it easy to isolate every single "button command." On top of that, two-way data binding dramatically simplified how real-time changes are mirrored on the screen while editing shapes. I know picking this specific stack might raise some eyebrows among frontend purists, but back then, I gave myself exactly 20 minutes to make a choice so I could just start building. My biggest fear was getting stuck in "paralysis by analysis" looking for the perfect tool and losing my momentum. Fortunately, it paid off—three years later, dedicating just a few hours a week, my passion for this hobby project is as strong as ever.&lt;/p&gt;

&lt;p&gt;When I started, I expected the browser's hardware limitations to quickly become a bottleneck. When you have thousands of objects (lines, arcs, splines) rendering simultaneously, everything still needs to feel buttery smooth. However, I discovered that the ceiling for optimization is practically limitless. Today, I'm convinced that a mid-range laptop can comfortably crunch up to 1 million elements without breaking a sweat. Thanks to a series of foundational optimizations, KulmanLab can already handle upwards of 100,000 objects seamlessly.&lt;/p&gt;

&lt;p&gt;The most exciting engineering challenges so far have been &lt;strong&gt;Grips (control points)&lt;/strong&gt; and &lt;strong&gt;Snapping (object snaps)&lt;/strong&gt;. In CAD software, you can't just click "somewhere close"—the engine must instantly and precisely snap to the exact center of a circle, the endpoint of a spline, or the intersection of two lines. To keep this running fast on large-scale drawings, I implemented spatial indexing. Instead of looping through all 100,000 objects with every single pixel movement of the mouse, the engine queries sorted arrays across X and Y coordinates. This spatial lookup keeps the performance locked at a stable 60 FPS, even on highly complex layouts.&lt;/p&gt;

&lt;p&gt;For insights, I rely on Google Analytics with custom events. This helps me track which tools and commands are used most frequently, completely respecting user privacy without ever touching or seeing the contents of their local files.&lt;/p&gt;

&lt;h3&gt;
  
  
  DXF: A File Format From Hell
&lt;/h3&gt;

&lt;p&gt;A lot of developers assume that parsing a DXF file is the hardest part. In reality, reading it is the easy piece. The true nightmare begins when you have to &lt;em&gt;export&lt;/em&gt; it—generating a perfectly valid file that won't throw errors in AutoCAD or cause a notoriously picky CNC controller to crash.&lt;/p&gt;

&lt;p&gt;To give you an idea, here is a snippet of a valid DXF structure. The format is a legacy, strict sequence of key-value pairs where every single key and value must occupy its own separate line. For instance, you might have a key of &lt;code&gt;4&lt;/code&gt; with an empty value, followed by key &lt;code&gt;1001&lt;/code&gt; paired with the value &lt;code&gt;AcadAnnotative&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;0&lt;br&gt;
STYLE&lt;br&gt;
5&lt;br&gt;
DC&lt;br&gt;
330&lt;br&gt;
3&lt;br&gt;
100&lt;br&gt;
AcDbSymbolTableRecord&lt;br&gt;
100&lt;br&gt;
AcDbTextStyleTableRecord&lt;br&gt;
2&lt;br&gt;
Annotative&lt;br&gt;
70&lt;br&gt;
0&lt;br&gt;
40&lt;br&gt;
0.0&lt;br&gt;
41&lt;br&gt;
1.0&lt;br&gt;
50&lt;br&gt;
0.0&lt;br&gt;
71&lt;br&gt;
0&lt;br&gt;
42&lt;br&gt;
0.2&lt;br&gt;
3&lt;br&gt;
arial.ttf&lt;br&gt;
4&lt;br&gt;
1001&lt;br&gt;
AcadAnnotative&lt;br&gt;
1000&lt;br&gt;
AnnotativeData&lt;br&gt;
1002&lt;br&gt;
{&lt;br&gt;
1070&lt;br&gt;
1&lt;br&gt;
1070&lt;br&gt;
1&lt;br&gt;
1002&lt;br&gt;
}&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As you can see, Autodesk has absolutely no problem duplicating identical keys for a single object, and empty rows acting as values are practically a "standard." The order of the entries matters—except when it doesn't. The whole format feels incredibly chaotic and unpredictable.&lt;/p&gt;

&lt;p&gt;Why is this so difficult? The real trap of DXF lies in its group codes. The exact same code can mean an X-coordinate for a line, a radius for a circle, or a layer name, completely depending on the context and the section you are in. On top of that, AutoCAD is notoriously sensitive to the sequence of these codes. If you accidentally mix up the order of parameters in the &lt;code&gt;HEADER&lt;/code&gt; or &lt;code&gt;TABLES&lt;/code&gt; section, your file instantly turns into garbage that no CAD editor on earth will open.&lt;/p&gt;

&lt;p&gt;Another massive challenge is supporting &lt;strong&gt;Splines&lt;/strong&gt;. While a basic line is defined by just two points, splines require control vertices (CVs), knot vectors, and weights. Autodesk’s official documentation covers this only superficially. In practice, I’ve had to do a lot of reverse-engineering on native AutoCAD files to figure out why a spline that renders flawlessly in my browser turns into a chaotic, mangled curve after being exported.&lt;/p&gt;

&lt;h3&gt;
  
  
  What KulmanLab Can Do Today: From MVP to a Working Tool
&lt;/h3&gt;

&lt;p&gt;Here is what is fully functional right now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Full 2D Primitive Support:&lt;/strong&gt; Straight lines, circles, arcs, ellipses, and polylines. Text rendering hasn't received as much attention yet, because handling fonts accurately in CAD engines is a whole separate rabbit hole. I plan to tackle that down the road.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Complex Splines:&lt;/strong&gt; This was my own little engineering victory. I implemented two curve-building modes: &lt;strong&gt;Fit points&lt;/strong&gt; (where the curve passes exactly through the specified coordinates) and &lt;strong&gt;Control Vertices (CV)&lt;/strong&gt; (where you shape the curve using a control polygon). This is absolutely critical for modeling intricate paths meant for laser cutting.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Transformation Tools:&lt;/strong&gt; The classic design toolkit you can't live without—&lt;strong&gt;Move, Copy, Scale, Mirror, and Rotate&lt;/strong&gt;. Every single one of them hooks perfectly into the object snapping logic.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Project Structuring:&lt;/strong&gt; Full support for &lt;strong&gt;Layers&lt;/strong&gt;. You can manage visibility, assign distinct colors, and set line types. This makes it easy to work on complex drawings where you need to separate your laser cutting profiles from engraving lines.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Precision and Insights:&lt;/strong&gt; The engine automatically calculates geometric intersection points and allows for precision scaling—essential baseline features for getting a drawing production-ready.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What’s Next? (The Roadmap)
&lt;/h3&gt;

&lt;p&gt;The project evolves alongside my primary backend job, but the roadmap is ambitious:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Enhanced Snapping:&lt;/strong&gt; Making object snaps even smarter and faster, including adding polar tracking.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;SVG Export:&lt;/strong&gt; While the primary focus remains DXF generation for machinery, being able to quickly save a blueprint as a SVG for printouts or documentation reports is an essential feature.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;UX Refinements:&lt;/strong&gt; I regularly analyze anonymized custom events in Google Analytics to see exactly where users stumble so I can continually streamline the workflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Primary Use Cases: Who is this for?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/volodymyr4509/kulmanlab" rel="noopener noreferrer"&gt;KulmanLab&lt;/a&gt; isn't trying to kill off AutoCAD. My goal is to provide a fast, nimble "Swiss Army knife" for people who just need to get a specific job done right here, right now, without waiting for a heavy engineering suite to load.&lt;/p&gt;

&lt;p&gt;I see two primary user bases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The CNC and Laser Cutting Community:&lt;/strong&gt; Makers, hobbyists, and shop operators who just need to quickly open a DXF file, verify dimensions, tweak a boundary, or clean up stray lines before sending the file to their machine controller.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Students and Technical Education:&lt;/strong&gt; Picture a student who needs to quickly draft an engineering schema for a lab report or term project. Instead of hunting down pirated software or jumping through hoops for a one-semester student license, they can just open their browser, sketch the diagram, and instantly export it as a high-resolution PNG or JPEG. It's incredibly fast and frictionless.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Why it fits the workflow:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Zero Latency:&lt;/strong&gt; You open the URL and you are drafting a second later.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Versatile Exports:&lt;/strong&gt; In addition to clean DXF output, you can generate crisp raster images to paste directly into report documents like Word or Google Docs.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Zero Barriers:&lt;/strong&gt; No sign-ups, no email verification, and no credit cards.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  AI as a Catalyst and Looking Ahead
&lt;/h3&gt;

&lt;p&gt;While KulmanLab has been in development for three years, the last six months have been a massive turning point. Leveraging modern AI coding assistants has drastically accelerated my output. For a solo developer, AI acts as a true force multiplier, turning what used to be a grueling, isolated grind into a fast-paced sprint toward new releases.&lt;/p&gt;

&lt;p&gt;Over-engineering and attempting to be everything to everyone is how good software becomes bloated and frustrating to use. Because of that, KulmanLab will stay true to its core philosophy: &lt;strong&gt;strictly 2D, strictly fast, and strictly in the browser&lt;/strong&gt;. No forced registration, no ad banners—just a clean, distraction-free tool that works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it out out here:&lt;/strong&gt; &lt;a href="https://kulmanlab.com" rel="noopener noreferrer"&gt;kulmanlab.com&lt;/a&gt;&lt;br&gt;
&lt;a href="https://kulmanlab.com" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fbadge%2FLive-kulmanlab.com-blue%3Fstyle%3Dflat-square" alt="Site" width="128" height="20"&gt;&lt;/a&gt;&lt;br&gt;
I would love to hear feedback from fellow developers and engineers in the comments! Have you ever had to wrestle with the DXF format in your own projects? What tools do you typically use when you just need to make a quick, painless edit to a drawing?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>cad</category>
      <category>angular</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
