<?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: xiang li</title>
    <description>The latest articles on DEV Community by xiang li (@xiang_li_0b9e2a3b7576ca68).</description>
    <link>https://dev.to/xiang_li_0b9e2a3b7576ca68</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%2F4164006%2F16757658-abf6-4b3c-9b14-2b1dfef9a5b5.png</url>
      <title>DEV Community: xiang li</title>
      <link>https://dev.to/xiang_li_0b9e2a3b7576ca68</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/xiang_li_0b9e2a3b7576ca68"/>
    <language>en</language>
    <item>
      <title>How to Spot AI-Generated Images in 2026: A Forensic Multi-Signal Guide (Midjourney, FLUX.1 &amp; DALL-E)</title>
      <dc:creator>xiang li</dc:creator>
      <pubDate>Mon, 05 Oct 2026 13:17:44 +0000</pubDate>
      <link>https://dev.to/xiang_li_0b9e2a3b7576ca68/how-to-spot-ai-generated-images-in-2026-a-forensic-multi-signal-guide-midjourney-flux1-dall-e-44ja</link>
      <guid>https://dev.to/xiang_li_0b9e2a3b7576ca68/how-to-spot-ai-generated-images-in-2026-a-forensic-multi-signal-guide-midjourney-flux1-dall-e-44ja</guid>
      <description>&lt;p&gt;As diffusion architectures like &lt;strong&gt;FLUX.1&lt;/strong&gt;, &lt;strong&gt;Midjourney v6&lt;/strong&gt;, and &lt;strong&gt;DALL-E 3&lt;/strong&gt; achieve photorealistic fidelity, distinguishing synthetic visuals from authentic camera photographs has become a critical challenge for journalists, fact-checkers, designers, and web developers.&lt;/p&gt;

&lt;p&gt;Many existing "AI detection APIs" operate as opaque black-box classifiers. They often cost thousands of dollars, suffer from high false-positive rates on compressed JPEGs, and require uploading sensitive user images to external third-party servers.&lt;/p&gt;

&lt;p&gt;In this guide, we break down a &lt;strong&gt;deterministic, multi-signal forensic approach&lt;/strong&gt; to inspecting digital images directly in the browser—analyzing metadata provenance, generative canvas geometry, and optical compression tells.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Signal One: Metadata &amp;amp; Provenance Traces (EXIF, XMP, C2PA)
&lt;/h2&gt;

&lt;p&gt;Before running complex computer vision algorithms, examine the raw byte stream of the image file. Generative models and camera sensors leave distinct digital fingerprints.&lt;/p&gt;

&lt;h3&gt;
  
  
  A. Midjourney &amp;amp; Stable Diffusion Chunk Signatures
&lt;/h3&gt;

&lt;p&gt;When downloaded directly or saved without heavy compression:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PNG &lt;code&gt;tEXt&lt;/code&gt; Chunks&lt;/strong&gt;: Stable Diffusion and ComfyUI write full generation parameters (Prompt, Negative Prompt, Seed, Sampler, CFG Scale) into the &lt;code&gt;parameters&lt;/code&gt; metadata chunk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Midjourney&lt;/strong&gt;: Often embeds &lt;code&gt;Description&lt;/code&gt; or software tags indicating the workflow version.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Lightweight PNG text chunk parser in vanilla JS&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;parsePngChunks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&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;view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DataView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Skip PNG header&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;byteLength&lt;/span&gt;&lt;span class="p"&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;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUint32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;offset&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;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromCharCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUint8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUint8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUint8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUint8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&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="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tEXt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;iTXt&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunkData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;length&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;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TextDecoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf-8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunkData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;chunks&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;h3&gt;
  
  
  B. C2PA Content Credentials &amp;amp; CAI
&lt;/h3&gt;

&lt;p&gt;Platforms like OpenAI (DALL-E 3) and Adobe Firefly embed &lt;strong&gt;C2PA (Coalition for Content Provenance and Authenticity)&lt;/strong&gt; metadata into the JPEG/PNG structure.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Look for JUMBF (JPEG Universal Metadata Box Format) boxes containing digital signatures (&lt;code&gt;c2pa.actions.v2&lt;/code&gt; stating &lt;code&gt;c2pa.created&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;If present, the image is mathematically certified as synthetic by the generator itself.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Signal Two: Generative Canvas Geometry &amp;amp; Resolution Fingerprints
&lt;/h2&gt;

&lt;p&gt;Diffusion models are trained on specific latent bucket resolutions. While humans crop photos arbitrarily, synthetic images published across the web frequently retain &lt;strong&gt;default canvas dimensions&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Default Resolution&lt;/th&gt;
&lt;th&gt;Aspect Ratio&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;FLUX.1 [dev/schnell]&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1024 × 1024, 832 × 1216&lt;/td&gt;
&lt;td&gt;1:1, 2:3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Midjourney v6 default&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1024 × 1024, 1456 × 816&lt;/td&gt;
&lt;td&gt;1:1, 16:9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DALL-E 3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1024 × 1024, 1792 × 1024&lt;/td&gt;
&lt;td&gt;1:1, 7:4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SDXL 1.0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1024 × 1024, 1152 × 896&lt;/td&gt;
&lt;td&gt;1:1, 9:7&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In contrast, real smartphone camera sensors shoot at standard hardware sensor ratios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;4:3 native&lt;/strong&gt; (e.g., iPhone 4032 × 3024, 48MP 8064 × 6048)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3:2 native&lt;/strong&gt; (DSLR full-frame sensors like Sony α7, Canon EOS)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If an uncropped image is exactly &lt;code&gt;1024x1024&lt;/code&gt; or &lt;code&gt;1456x816&lt;/code&gt; without camera EXIF, the probability of generative origin increases dramatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Signal Three: The Social Media Stripping Dilemma
&lt;/h2&gt;

&lt;p&gt;A common failure mode for detection tools: &lt;strong&gt;Social media platforms (X / Twitter, Reddit, Instagram, Facebook) automatically re-encode uploads and strip all EXIF / C2PA metadata&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When metadata is stripped, naive tools give up or report "Inconclusive". A forensic analysis must check secondary visual tells:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;High-Frequency Over-Smoothing&lt;/strong&gt;: FLUX and Midjourney often generate hyper-smooth skin transitions with synthetic micro-noise superimposed rather than true optical camera noise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pupil &amp;amp; Specular Reflection Symmetry&lt;/strong&gt;: Zoom in on eyes. Natural photos reflect the ambient lighting environment accurately across both eyes. Many diffusion models still hallucinate mismatched light sources or distorted pupil boundaries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Background Text &amp;amp; Glyph Coherence&lt;/strong&gt;: Examine signage or license plates in the background. Synthetic text often mimics typographic form without spelling legible words.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  4. Building a Privacy-First Verification Workflow
&lt;/h2&gt;

&lt;p&gt;Uploading every image you come across to a remote server exposes private photos and introduces network latency.&lt;/p&gt;

&lt;p&gt;To solve this, we built &lt;strong&gt;&lt;a href="https://checkaifree.com" rel="noopener noreferrer"&gt;Check AI Free&lt;/a&gt;&lt;/strong&gt; — a free, 100% client-side forensic inspection utility that evaluates metadata, canvas geometry, and visual tells directly in your browser:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero Server Uploads&lt;/strong&gt;: Image bytes are analyzed locally via the Web File API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instant Multi-Signal Dossier&lt;/strong&gt;: Breaks down prompt traces, EXIF presence, resolution matching, and social media compression status.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1-Click Right-Click Inspection&lt;/strong&gt;: We recently packaged this into the &lt;strong&gt;&lt;a href="https://checkaifree.com" rel="noopener noreferrer"&gt;Check AI Free Chrome Extension&lt;/a&gt;&lt;/strong&gt;, allowing you to right-click any image across X, Reddit, or news feeds and immediately view forensic signals without leaving your tab.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary Checklist
&lt;/h2&gt;

&lt;p&gt;When inspecting an image online:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;✅ Check file headers for PNG parameter chunks or C2PA JUMBF manifests.&lt;/li&gt;
&lt;li&gt;✅ Compare canvas dimensions against known latent diffusion buckets (1024x1024, 1456x816).&lt;/li&gt;
&lt;li&gt;✅ Look for missing camera sensor metadata (focal length, ISO, aperture) on high-res photos.&lt;/li&gt;
&lt;li&gt;✅ Scrutinize optical coherence (specular reflections, text glyphs, ear/finger contours).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can test any image today at &lt;strong&gt;&lt;a href="https://checkaifree.com" rel="noopener noreferrer"&gt;Check AI Free&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;What techniques or edge cases do you look for when debunking synthetic photos? Let's discuss in the comments below!&lt;/p&gt;

</description>
      <category>machinelearning</category>
    </item>
  </channel>
</rss>
