<?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: Auviel</title>
    <description>The latest articles on DEV Community by Auviel (@auviel).</description>
    <link>https://dev.to/auviel</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3226731%2F2f349c63-2c3a-4f2a-a487-782135b3cdbb.png</url>
      <title>DEV Community: Auviel</title>
      <link>https://dev.to/auviel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/auviel"/>
    <language>en</language>
    <item>
      <title>Building ImageMark: A Privacy-First Watermarking Tool That Runs Entirely in Your Browser</title>
      <dc:creator>Auviel</dc:creator>
      <pubDate>Fri, 30 May 2025 15:14:14 +0000</pubDate>
      <link>https://dev.to/auviel/building-imagemark-a-privacy-first-watermarking-tool-that-runs-entirely-in-your-browser-4b4m</link>
      <guid>https://dev.to/auviel/building-imagemark-a-privacy-first-watermarking-tool-that-runs-entirely-in-your-browser-4b4m</guid>
      <description>&lt;p&gt;How I solved a common content creator problem using vanilla JavaScript and client-side processing&lt;br&gt;
The Problem That Started It All&lt;br&gt;
As a developer, I kept hearing the same complaints from photographer and designer friends:&lt;/p&gt;

&lt;p&gt;"Why do watermarking tools cost $20/month for something so simple?"&lt;br&gt;
"I don't want to upload my client's photos to some random website"&lt;br&gt;
"Batch processing is locked behind premium tiers"&lt;/p&gt;

&lt;p&gt;Sound familiar? I thought so too. So I decided to build something better.&lt;br&gt;
Enter ImageMark 🚀&lt;br&gt;
ImageMark is a completely client-side watermarking tool that processes your images without ever sending them to a server. No accounts, no subscriptions, no privacy concerns.&lt;br&gt;
🔗 &lt;a href="https://imagemark.app" rel="noopener noreferrer"&gt;Try it live &lt;/a&gt;| 📁 &lt;a href="https://github.com/codebyvalentine/imagemark" rel="noopener noreferrer"&gt;GitHub repo&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%2Fhzn86c12fo6ely3v6qhr.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.amazonaws.com%2Fuploads%2Farticles%2Fhzn86c12fo6ely3v6qhr.png" alt="Image description" width="800" height="482"&gt;&lt;/a&gt;&lt;br&gt;
The Tech Stack (Keeping It Simple)&lt;br&gt;
I deliberately chose vanilla JavaScript over frameworks to keep the bundle size small and loading fast:&lt;/p&gt;

&lt;p&gt;Vanilla JavaScript - No framework overhead&lt;br&gt;
Canvas API - For image manipulation and watermark rendering&lt;br&gt;
Web Workers - Non-blocking batch processing&lt;br&gt;
FileReader API - Client-side file handling&lt;br&gt;
CSS Grid - Responsive layout&lt;/p&gt;

&lt;p&gt;Key Technical Challenges &amp;amp; Solutions&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Client-Side Image Processing
The biggest challenge was handling image manipulation entirely in the browser. Here's how the core watermarking function works:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function applyWatermark(imageData, watermarkText, options) {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');

  // Set canvas dimensions to match image
  canvas.width = imageData.width;
  canvas.height = imageData.height;

  // Draw original image
  ctx.drawImage(imageData, 0, 0);

  // Smart positioning based on brightness analysis
  const position = calculateOptimalPosition(ctx, canvas.width, canvas.height);

  // Apply watermark with proper opacity and blending
  ctx.globalAlpha = options.opacity;
  ctx.font = `${options.fontSize}px Arial`;
  ctx.fillStyle = options.color;
  ctx.fillText(watermarkText, position.x, position.y);

  return canvas.toDataURL();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Smart Watermark Placement
Instead of just slapping watermarks in corners, I implemented brightness analysis to find the optimal placement:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function calculateOptimalPosition(ctx, width, height) {
  // Sample different regions of the image
  const regions = [
    { x: width * 0.1, y: height * 0.1 },    // Top-left
    { x: width * 0.9, y: height * 0.1 },    // Top-right  
    { x: width * 0.1, y: height * 0.9 },    // Bottom-left
    { x: width * 0.9, y: height * 0.9 }     // Bottom-right
  ];

  let bestRegion = regions[0];
  let bestContrast = 0;

  regions.forEach(region =&amp;gt; {
    const brightness = getRegionBrightness(ctx, region.x, region.y);
    const contrast = calculateContrast(brightness, watermarkColor);

    if (contrast &amp;gt; bestContrast) {
      bestContrast = contrast;
      bestRegion = region;
    }
  });

  return bestRegion;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Batch Processing with Web Workers
To keep the UI responsive during batch operations, I offloaded the heavy lifting to Web Workers:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Main thread
const worker = new Worker('watermark-worker.js');

worker.postMessage({
  images: selectedFiles,
  watermarkOptions: userSettings
});

worker.onmessage = (event) =&amp;gt; {
  const { processedImage, progress } = event.data;
  updateProgressBar(progress);
  downloadProcessedImage(processedImage);
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Privacy by Design&lt;br&gt;
The entire application follows privacy-first principles:&lt;/p&gt;

&lt;p&gt;✅ No server uploads - Images never leave your device&lt;br&gt;
✅ No user accounts - Start using immediately&lt;br&gt;
✅ No tracking - Zero analytics or data collection&lt;br&gt;
✅ Open source - Full transparency in code&lt;/p&gt;

&lt;p&gt;Performance Optimizations&lt;br&gt;
File Size Matters&lt;/p&gt;

&lt;p&gt;Vanilla JS keeps the bundle under 50KB gzipped&lt;br&gt;
Lazy loading for non-critical features&lt;br&gt;
Efficient canvas operations to minimize memory usage&lt;/p&gt;

&lt;p&gt;Mobile Responsiveness&lt;/p&gt;

&lt;p&gt;Touch-friendly drag &amp;amp; drop zones&lt;br&gt;
Responsive design that works on tablets&lt;br&gt;
Optimized for mobile browsers&lt;/p&gt;

&lt;p&gt;Lessons Learned&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Canvas API Gotchas
Working with different image formats revealed several browser inconsistencies. EXIF data handling was particularly tricky across different devices.&lt;/li&gt;
&lt;li&gt;User Experience is Everything
Technical capabilities mean nothing if users can't figure out how to use your tool. I spent as much time on UX as on the actual watermarking logic.&lt;/li&gt;
&lt;li&gt;Progressive Enhancement Works
Starting with core functionality and adding features incrementally made development much smoother.
What's Next?
Current roadmap includes:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Custom watermark templates&lt;br&gt;
 Video watermarking support&lt;br&gt;
 Advanced positioning controls&lt;br&gt;
 Export format options&lt;/p&gt;

&lt;p&gt;Try It Out!&lt;br&gt;
ImageMark is live at imagemark.app and the full source code is available on GitHub.&lt;br&gt;
I'd love to hear your feedback! What features would make this more useful for your workflow?&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
