<?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: Elliot Silver</title>
    <description>The latest articles on DEV Community by Elliot Silver (@elliot_silver).</description>
    <link>https://dev.to/elliot_silver</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%2F1292437%2F0c227669-4d62-4cce-a388-de0d99a294e7.png</url>
      <title>DEV Community: Elliot Silver</title>
      <link>https://dev.to/elliot_silver</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/elliot_silver"/>
    <language>en</language>
    <item>
      <title>Compact Design: a JSON language models can write and Figma can import</title>
      <dc:creator>Elliot Silver</dc:creator>
      <pubDate>Fri, 14 Aug 2026 07:57:47 +0000</pubDate>
      <link>https://dev.to/elliot_silver/compact-design-a-json-language-models-can-write-and-figma-can-import-gan</link>
      <guid>https://dev.to/elliot_silver/compact-design-a-json-language-models-can-write-and-figma-can-import-gan</guid>
      <description>&lt;p&gt;Don't ask a model to speak Figma's plugin API. Don't ask it for CSS and hope a screenshot is a design.&lt;/p&gt;

&lt;p&gt;I wanted a format a model can write, a schema can reject, and Figma can turn into native, editable frames.&lt;/p&gt;

&lt;p&gt;That's Compact Design.&lt;/p&gt;

&lt;p&gt;It's a small JSON language. &lt;code&gt;@compact-design/core&lt;/code&gt; validates, normalizes, lints, and patches it. The Figma plugin is the first adapter. Core never depends on Figma.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/Elliot727/compact-design" rel="noopener noreferrer"&gt;github.com/Elliot727/compact-design&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A card looks like this
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"canvas"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"desktop"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Desktop"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1440&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"height"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fill"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#F8F5EE"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"nodes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"card"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FRAME"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"x"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"y"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"w"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;320&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"h"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"fill"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#FFFFFF"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"cornerRadius"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"layout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"direction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"VERTICAL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"padding"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"itemSpacing"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"children"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TEXT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"w"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;272&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"h"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A quieter kind of stay."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"font"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"family"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Arial"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"style"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bold"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"fill"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#23221E"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Required on a node: w and h. type defaults to FRAME. IDs are generated if you omit them — give anything you'll update later an explicit one.&lt;/p&gt;

&lt;p&gt;Hex fills. HUG / FILL / FIXED. Angle gradients. Elevation presets. Variables, styles, components, prototypes, images. One current schema. No version soup.&lt;/p&gt;

&lt;p&gt;This is the input API. Not Figma REST. Not a properties wrapper. Not 24px.&lt;/p&gt;

&lt;p&gt;What the engine actually does&lt;/p&gt;

&lt;p&gt;Compact Design JSON&lt;br&gt;
        ↓&lt;br&gt;
@compact-design/core&lt;br&gt;
  schema → normalize → semantic checks → lint / patch&lt;br&gt;
        ↓&lt;br&gt;
adapter&lt;br&gt;
        ↓&lt;br&gt;
Figma today; another tool can implement the same language&lt;/p&gt;

&lt;p&gt;Core is ordinary TypeScript. No figma.*, no plugin typings.&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;applyPatch&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@compact-design/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&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="nx"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;lint&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="nb"&gt;document&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;Issues come back structured, which is the point if a model is in the loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"valid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"issues"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ERROR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SCHEMA_VALIDATION"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nodes[0].children[2].layout.direction"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"must be HORIZONTAL, VERTICAL, or GRID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"suggestion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Use the allowed property type or enum from compact-design.schema.json."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Schema and semantic errors block import. Lint does not. Lint is advisory: contrast, 44px targets, 4px spacing, missing Auto Layout, hard-coded colours, broken prototype destinations.&lt;/p&gt;

&lt;p&gt;The plugin does not render a browser thumbnail. A thumbnail would lie about fonts, Auto Layout, masks, and effects. You get counts, matches, and the repair JSON instead.&lt;/p&gt;

&lt;p&gt;Create, update, patch, export&lt;/p&gt;

&lt;p&gt;Every imported canvas and layer stores its JSON id as plugin data.&lt;/p&gt;

&lt;p&gt;• Create new refuses a canvas ID already on the page.&lt;br&gt;
• Update matching IDs replaces matching canvases in place.&lt;br&gt;
• Replace matching canvases rebuilds them from the document you just supplied.&lt;br&gt;
• Export selection writes Compact Design JSON back out.&lt;/p&gt;

&lt;p&gt;Full-document update is canvas-granular. A patch is node-granular. Failures roll back — the previous design is cloned first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"patch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"operations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"op"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"set"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"set"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The same design.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;A different mood."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"op"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"remove"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"old-badge"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"op"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"append"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"parent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"card"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"note"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TEXT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"w"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;272&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"h"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UPDATED WITH A JSON PATCH"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"fill"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#B69B62"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;set changes only what you list. Everything else stays put.&lt;/p&gt;

&lt;p&gt;Try it&lt;/p&gt;

&lt;p&gt;The package is not on npm yet. Build from the repo.&lt;/p&gt;

&lt;p&gt;git clone &lt;a href="https://github.com/Elliot727/compact-design.git" rel="noopener noreferrer"&gt;https://github.com/Elliot727/compact-design.git&lt;/a&gt;&lt;br&gt;
cd compact-design&lt;br&gt;
npm install&lt;br&gt;
npm run build&lt;/p&gt;

&lt;p&gt;In Figma desktop: Plugins → Development → Import plugin from manifest… and pick plugins/figma/manifest.json.&lt;/p&gt;

&lt;p&gt;Then paste &lt;a href="https://github.com/Elliot727/compact-design/blob/main/examples/design-language-showcase.json" rel="noopener noreferrer"&gt;examples/design-language-showcase.json&lt;/a&gt;. It should land as a finished product screen, not an API reference board.&lt;/p&gt;

&lt;p&gt;Language docs, schema, and the other fixtures live next to it.&lt;/p&gt;

&lt;p&gt;If you generate UI with a model, this is the format I'd actually trust.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Elliot727/compact-design" rel="noopener noreferrer"&gt;MPL-2.0. github.com/Elliot727/compact-design&lt;/a&gt;&lt;/p&gt;

</description>
      <category>figma</category>
      <category>llm</category>
      <category>json</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Your First Real Project – Live Edge Detection with Recording</title>
      <dc:creator>Elliot Silver</dc:creator>
      <pubDate>Wed, 31 Dec 2025 17:00:00 +0000</pubDate>
      <link>https://dev.to/elliot_silver/your-first-real-project-live-edge-detection-with-recording-2fm8</link>
      <guid>https://dev.to/elliot_silver/your-first-real-project-live-edge-detection-with-recording-2fm8</guid>
      <description>&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Edge detection is the "hello world" of serious computer vision – it's where you start tuning parameters, chasing clean lines, and realizing how much time raw OpenCV setups waste.&lt;/p&gt;

&lt;p&gt;With vanilla GoCV you'd spend dozens of lines on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Camera capture loop&lt;/li&gt;
&lt;li&gt;Window creation&lt;/li&gt;
&lt;li&gt;Mat management (and inevitable leaks)&lt;/li&gt;
&lt;li&gt;Keyboard handling&lt;/li&gt;
&lt;li&gt;Recording logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GoCVKit gives you all that for free. You focus only on the pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hands-On: Build the Edge Detector
&lt;/h2&gt;

&lt;p&gt;Start from the quick start (if you followed Week #2, you already have this):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# If not done yet&lt;/span&gt;
&lt;span class="nb"&gt;mkdir &lt;/span&gt;gocvkit-edge &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;gocvkit-edge
go mod init edge-demo
go get github.com/Elliot727/gocvkit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="err"&gt;–&lt;/span&gt; &lt;span class="n"&gt;same&lt;/span&gt; &lt;span class="n"&gt;as&lt;/span&gt; &lt;span class="n"&gt;always&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/Elliot727/gocvkit"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;gocvkit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"config.toml"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;nil&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;Now create the star of the show – &lt;code&gt;config.toml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[app]&lt;/span&gt;
&lt;span class="py"&gt;window_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"GoCVKit – Live Edge Detection"&lt;/span&gt;
&lt;span class="py"&gt;record&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;                  &lt;span class="c"&gt;# Save processed video automatically&lt;/span&gt;
&lt;span class="py"&gt;output&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"edge_detection.mp4"&lt;/span&gt;  &lt;span class="c"&gt;# Optional: custom filename&lt;/span&gt;

&lt;span class="nn"&gt;[camera]&lt;/span&gt;
&lt;span class="py"&gt;device_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="c"&gt;# file = "demo.mp4"            # Uncomment to use a video file instead&lt;/span&gt;

&lt;span class="nn"&gt;[stream]&lt;/span&gt;
&lt;span class="py"&gt;enabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;                &lt;span class="c"&gt;# Keep off for now – we can enable later&lt;/span&gt;

&lt;span class="nn"&gt;[[pipeline.steps]]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Grayscale"&lt;/span&gt;

&lt;span class="nn"&gt;[[pipeline.steps]]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"GaussianBlur"&lt;/span&gt;
&lt;span class="py"&gt;kernel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;
&lt;span class="py"&gt;sigma&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.8&lt;/span&gt;

&lt;span class="nn"&gt;[[pipeline.steps]]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Canny"&lt;/span&gt;
&lt;span class="py"&gt;low&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;
&lt;span class="py"&gt;high&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go run &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Boom – live edge detection window with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clean Canny edges&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;f&lt;/code&gt; to toggle FPS overlay&lt;/li&gt;
&lt;li&gt;Recording started automatically (check your folder for &lt;code&gt;edge_detection.mp4&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Edit any value in &lt;code&gt;config.toml&lt;/code&gt; → save → instant update (thanks Week #2!)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Level It Up
&lt;/h2&gt;

&lt;p&gt;Try these live tweaks (no restart needed):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Crank the blur for smoother edges:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;   &lt;span class="py"&gt;kernel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;
   &lt;span class="py"&gt;sigma&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Softer edges:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;   &lt;span class="py"&gt;low&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;
   &lt;span class="py"&gt;high&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Add morphology to clean up noise:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;   &lt;span class="nn"&gt;[[pipeline.steps]]&lt;/span&gt;
   &lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Dilate"&lt;/span&gt;
   &lt;span class="py"&gt;kernel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
   &lt;span class="py"&gt;iterations&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Toggle recording on/off live by flipping &lt;code&gt;record = false&lt;/code&gt; → save.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Bonus: Add a Simple Overlay with Frame Callback
&lt;/h2&gt;

&lt;p&gt;Want to show current thresholds on screen? Add a tiny callback to &lt;code&gt;main.go&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;gocv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Runs on the final processed frame&lt;/span&gt;
    &lt;span class="n"&gt;gocv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PutText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Low: 50  High: 150"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;gocv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FontHersheyPlain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gocv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewScalar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;2&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;(Recompile once – but now you can hot-reload the text by changing the string and saving config if you make it config-driven later.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Pro Tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Recording auto-rotates files if they get too big – no manual management.&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;Esc&lt;/code&gt; or &lt;code&gt;q&lt;/code&gt; to quit gracefully (resources cleaned up).&lt;/li&gt;
&lt;li&gt;Run out of webcam? Swap to a video file by uncommenting &lt;code&gt;file = "demo.mp4"&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;Copy the config above, run it, and start playing. Break it. Fix it live. Feel the speed.&lt;/p&gt;

&lt;p&gt;What's your favorite edge detection tweak? Do you prefer strong/thin lines or softer outlines? Drop it in the comments!&lt;/p&gt;

</description>
      <category>computervision</category>
      <category>go</category>
      <category>opensource</category>
      <category>programming</category>
    </item>
    <item>
      <title>The Hot-Reload Magic - Tweak Pipelines Live (No Restarts!)</title>
      <dc:creator>Elliot Silver</dc:creator>
      <pubDate>Wed, 17 Dec 2025 17:00:00 +0000</pubDate>
      <link>https://dev.to/elliot_silver/the-hot-reload-magic-tweak-pipelines-live-no-restarts-1noe</link>
      <guid>https://dev.to/elliot_silver/the-hot-reload-magic-tweak-pipelines-live-no-restarts-1noe</guid>
      <description>&lt;p&gt;Edit your &lt;code&gt;config.toml&lt;/code&gt; while the app is running and watch the pipeline update instantly. No recompiling. No stopping the camera. Pure iteration bliss.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;If you've ever prototyped with raw GoCV (or any OpenCV binding), you know the pain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Change a threshold or kernel size? Edit code → recompile → restart → wait for camera init.&lt;/li&gt;
&lt;li&gt;Takes 10-30 seconds per tweak → kills your flow.&lt;/li&gt;
&lt;li&gt;Accidentally leak a Mat? Crash and start over.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GoCVKit fixes this with &lt;strong&gt;live config hot-reload&lt;/strong&gt;. Save &lt;code&gt;config.toml&lt;/code&gt; → the running app detects the change → rebuilds the entire pipeline on the fly → you see the result immediately.&lt;/p&gt;

&lt;p&gt;It's the closest thing Go has to Jupyter notebooks for computer vision.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works (The Magic Explained)
&lt;/h2&gt;

&lt;p&gt;Under the hood it's simple and robust:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A lightweight file watcher (using &lt;code&gt;fsnotify&lt;/code&gt;) monitors &lt;code&gt;config.toml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;On any save, the config is re-parsed.&lt;/li&gt;
&lt;li&gt;The pipeline builder recreates all processors with new parameters.&lt;/li&gt;
&lt;li&gt;The next frame uses the brand-new pipeline – no frames dropped, no allocations outside the double buffer.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Zero boilerplate for you. Zero leaks guaranteed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hands-On: See It Yourself
&lt;/h2&gt;

&lt;p&gt;Fire up the quick start if you haven't already:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;gocvkit-demo &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;gocvkit-demo
go mod init demo
go get github.com/Elliot727/gocvkit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;main.go&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import (
    "log"
    "github.com/Elliot727/gocvkit"
)

func main() {
    app, err := gocvkit.NewApp("config.toml")
    if err != nil {
        log.Fatal(err)
    }
    defer app.Close()
    app.Run(nil)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Create initial config
&lt;/h1&gt;

&lt;p&gt;config.toml&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[app]
window_name = "GoCVKit – Hot-Reload Demo"

[[pipeline.steps]]
name = "Grayscale"

[[pipeline.steps]]
name = "Canny"
low = 50
high = 150
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the fun part:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Window opens showing live Canny edges.&lt;/li&gt;
&lt;li&gt;Open &lt;code&gt;config.toml&lt;/code&gt; in your editor.&lt;/li&gt;
&lt;li&gt;Change &lt;code&gt;high = 150&lt;/code&gt; → &lt;code&gt;high = 300&lt;/code&gt; → save.
→ Edges instantly thicken.&lt;/li&gt;
&lt;li&gt;Replace the entire Canny block with GaussianBlur:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[[pipeline.steps]]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"GaussianBlur"&lt;/span&gt;
&lt;span class="py"&gt;kernel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;
&lt;span class="py"&gt;sigma&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save → blur appears instantly.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Try something wild:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[[pipeline.steps]]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Grayscale"&lt;/span&gt;

&lt;span class="nn"&gt;[[pipeline.steps]]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"MedianBlur"&lt;/span&gt;
&lt;span class="py"&gt;k&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No restarts. No code changes. Just pure experimentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pro Tips for Hot-Reload Happiness
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Typos? GoCVKit prints clear errors in the terminal but keeps the old pipeline running – no crash.&lt;/li&gt;
&lt;li&gt;Add/remove steps freely – the builder handles it gracefully.&lt;/li&gt;
&lt;li&gt;Want to test recording too? Flip &lt;code&gt;record = true&lt;/code&gt; live and it starts/stops without hassle.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The pipeline itself uses double-buffering so frame processing never blocks or allocates per frame. That's why you get smooth FPS even during reloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;Grab the repo, run the example above, and go wild with the config. Break it on purpose – you'll see how forgiving it is.&lt;/p&gt;

&lt;p&gt;What's the first parameter you always tweak in edge detection? Thresholds? Kernel sizes? Tell me in the comments!&lt;/p&gt;

</description>
      <category>computervision</category>
      <category>go</category>
      <category>opensource</category>
      <category>programming</category>
    </item>
    <item>
      <title>Introducing GoCVKit: Zero-Boilerplate Computer Vision in Go</title>
      <dc:creator>Elliot Silver</dc:creator>
      <pubDate>Wed, 10 Dec 2025 17:34:35 +0000</pubDate>
      <link>https://dev.to/elliot_silver/introducing-gocvkit-zero-boilerplate-computer-vision-in-go-gbd</link>
      <guid>https://dev.to/elliot_silver/introducing-gocvkit-zero-boilerplate-computer-vision-in-go-gbd</guid>
      <description>&lt;p&gt;Hey there, fellow Gophers! If you’ve worked with computer vision in Go, you know GoCV is fantastic for accessing OpenCV’s power.&lt;/p&gt;

&lt;p&gt;But the reality? Boilerplate everywhere: camera setup, Mat management, window handling, resource leaks, and recompiling just to tweak a parameter. It’s not exactly “fun.”&lt;/p&gt;

&lt;p&gt;That’s why I created GoCVKit—a modular framework that makes real-time CV prototyping smooth, efficient, and genuinely enjoyable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is GoCVKit?&lt;/strong&gt;&lt;br&gt;
GoCVKit is a clean, idiomatic layer on top of GoCV for live camera or video streams. It handles the heavy lifting so you can focus on ideas, not plumbing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key features:&lt;/strong&gt;&lt;br&gt;
•  Zero boilerplate: Full apps in ≤10 lines.&lt;br&gt;
•  Hot-reload config: Edit config.toml and changes apply instantly—no restarts.&lt;br&gt;
•  Performance-focused: Double-buffered pipelines with zero per-frame allocations.&lt;br&gt;
•  Extensible: Built-in processors (Grayscale, GaussianBlur, Canny, Sobel, etc.) plus easy custom filters.&lt;br&gt;
•  Quality-of-life extras: Video recording, toggleable FPS overlay, frame callbacks, graceful shutdown, and seamless input switching (webcam or file).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why I Built This&lt;/strong&gt;&lt;br&gt;
Go is perfect for CV: fast, concurrent, and easy to deploy. But raw GoCV meant rewriting the same scaffolding repeatedly. GoCVKit eliminates that pain, making it ideal for:&lt;br&gt;
•  Rapid prototyping&lt;br&gt;
•  Teaching and demos&lt;br&gt;
•  Live presentations&lt;br&gt;
•  Real-time vision apps&lt;br&gt;
•  Anyone who wants to stay sane while experimenting&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get Started&lt;/strong&gt;&lt;br&gt;
go get github.com/Elliot727/gocvkit&lt;/p&gt;

&lt;p&gt;Head to the repo for full documentation, processor list, and custom filter guides:&lt;br&gt;
github.com/Elliot727/gocvkit&lt;/p&gt;

&lt;p&gt;Star it if it saves you time, contribute if you make it better, and tell your friends—Go deserves a first-class CV experience! 🚀&lt;br&gt;
What real-time CV projects are you working on? Let me know in the comments!&lt;/p&gt;

</description>
      <category>computervision</category>
      <category>go</category>
      <category>opensource</category>
      <category>programming</category>
    </item>
    <item>
      <title>Hey Go devs! Built GoCVKit – a clean, zero-boilerplate OpenCV framework for Go. Hot-reload config, modular pipelines, no leaks, fun live CV apps! 🚀
github.com/Elliot727/gocvkit
Star if you like! #golang #opencv</title>
      <dc:creator>Elliot Silver</dc:creator>
      <pubDate>Wed, 10 Dec 2025 17:30:07 +0000</pubDate>
      <link>https://dev.to/elliot_silver/hey-go-devs-built-gocvkit-a-clean-zero-boilerplate-opencv-framework-for-go-hot-reload-config-4jgg</link>
      <guid>https://dev.to/elliot_silver/hey-go-devs-built-gocvkit-a-clean-zero-boilerplate-opencv-framework-for-go-hot-reload-config-4jgg</guid>
      <description></description>
    </item>
  </channel>
</rss>
