<?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: Prabhash</title>
    <description>The latest articles on DEV Community by Prabhash (@prabhash_me).</description>
    <link>https://dev.to/prabhash_me</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%2F4139255%2Fb60888a9-49b3-48d2-b954-cc63fa960405.png</url>
      <title>DEV Community: Prabhash</title>
      <link>https://dev.to/prabhash_me</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/prabhash_me"/>
    <language>en</language>
    <item>
      <title>The WAV header is not 44 bytes</title>
      <dc:creator>Prabhash</dc:creator>
      <pubDate>Fri, 23 Oct 2026 20:25:20 +0000</pubDate>
      <link>https://dev.to/prabhash_me/the-wav-header-is-not-44-bytes-52la</link>
      <guid>https://dev.to/prabhash_me/the-wav-header-is-not-44-bytes-52la</guid>
      <description>&lt;p&gt;Most WAV bugs I have seen come from this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;samples&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;44 is the length of the smallest legal WAV: 12 bytes of RIFF header, a 24-byte &lt;code&gt;fmt&lt;/code&gt; chunk, then the 8-byte header of the &lt;code&gt;data&lt;/code&gt; chunk. So it is right for that one file. It is not the format.&lt;/p&gt;

&lt;p&gt;WAV is RIFF, and RIFF is a list of chunks. Each chunk is a 4-byte id, a 4-byte little-endian size, then that many bytes, padded to an even length. The &lt;code&gt;data&lt;/code&gt; chunk can sit anywhere in the list, and anything the writer added goes in front of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Five files, four offsets
&lt;/h2&gt;

&lt;p&gt;These are from &lt;a href="https://loremfile.dev/wav" rel="noopener noreferrer"&gt;loremfile.dev&lt;/a&gt;, a CC0 fixture set I maintain. I parsed each one by walking its chunks.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;fmt&lt;/code&gt; body&lt;/th&gt;
&lt;th&gt;Before &lt;code&gt;data&lt;/code&gt;
&lt;/th&gt;
&lt;th&gt;Samples start&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://loremfile.dev/wav/sine-440hz-3s-44k-16bit-stereo.wav" rel="noopener noreferrer"&gt;16-bit stereo&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;16 bytes&lt;/td&gt;
&lt;td&gt;nothing&lt;/td&gt;
&lt;td&gt;44&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://loremfile.dev/wav/sine-440hz-3s-44k-8bit-stereo.wav" rel="noopener noreferrer"&gt;8-bit stereo&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;16 bytes&lt;/td&gt;
&lt;td&gt;nothing&lt;/td&gt;
&lt;td&gt;44&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://loremfile.dev/wav/sine-440hz-3s-44k-32bit-float-stereo.wav" rel="noopener noreferrer"&gt;32-bit float&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;18 bytes&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;fact&lt;/code&gt;, 12 bytes&lt;/td&gt;
&lt;td&gt;58&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://loremfile.dev/wav/sine-440hz-3s-48k-24bit-stereo.wav" rel="noopener noreferrer"&gt;24-bit, 48 kHz&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;40 bytes&lt;/td&gt;
&lt;td&gt;nothing&lt;/td&gt;
&lt;td&gt;68&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://loremfile.dev/wav/with-list-chunk-3s.wav" rel="noopener noreferrer"&gt;16-bit with tags&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;16 bytes&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;LIST&lt;/code&gt;, 50 bytes&lt;/td&gt;
&lt;td&gt;94&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Same format, same 440 Hz sine wave, four different answers. Three of the five break &lt;code&gt;data[44:]&lt;/code&gt;. None of them is unusual: a float export, a 24-bit export, and a file with a title and artist in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The header length is not fixed
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;fmt&lt;/code&gt; is 16 bytes for plain PCM, 18 when the writer adds the &lt;code&gt;cbSize&lt;/code&gt; field, and 40 for &lt;code&gt;WAVE_FORMAT_EXTENSIBLE&lt;/code&gt;. That moves the samples on its own.&lt;/p&gt;

&lt;p&gt;Other chunks move them further. Non-PCM files should carry a &lt;code&gt;fact&lt;/code&gt; chunk with the sample count, which puts the float file at 58. Metadata goes in &lt;code&gt;LIST&lt;/code&gt;/&lt;code&gt;INFO&lt;/code&gt;, which puts the tagged file at 94. Read the sizes and skip what you do not recognise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bit depth does not tell you the sample type
&lt;/h2&gt;

&lt;p&gt;The format tag does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;1&lt;/code&gt; is integer PCM&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;3&lt;/code&gt; is IEEE float&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0xFFFE&lt;/code&gt; is extensible, and the real format is a subformat 24 bytes into the chunk body&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The float file reports 32 bits with tag &lt;code&gt;3&lt;/code&gt;. Read those bytes as 32-bit integers and every sample comes out huge, because you are reading an exponent as a magnitude.&lt;/p&gt;

&lt;p&gt;The 24-bit file is the other half of this. Its tag is &lt;code&gt;0xFFFE&lt;/code&gt;, so code that switches on the top-level tag gives up on it, even though the subformat says plain PCM.&lt;/p&gt;

&lt;h2&gt;
  
  
  8-bit is unsigned
&lt;/h2&gt;

&lt;p&gt;Everything above 8-bit is signed. Same sine wave, same instant, three of the files above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;8-bit:   128, 128, 128, 128, 129, 129, 130, 130
16-bit:  0, 0, 181, 181, 361, 361, 541, 541
float:   0.0, 0.0, 0.0055, 0.0055, 0.011, 0.011
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Silence is 128 in the first and 0 in the other two. Read the 8-bit file as signed and silence becomes a full-scale offset.&lt;/p&gt;

&lt;h2&gt;
  
  
  Walking the chunks
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wav_chunks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Yield (chunk_id, body_offset, body_size) for every top-level RIFF chunk.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RIFF&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;WAVE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;not a RIFF/WAVE file&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;chunk_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;pos&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="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unpack_from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;I&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pos&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;chunk_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pos&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="n"&gt;size&lt;/span&gt;
        &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# chunks are padded to an even length
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wav_samples&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;The format tag, bit depth and sample bytes, wherever they turned out to be.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;chunk_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;wav_chunks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&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;chunk_id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fmt &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unpack_from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;HHIIHH&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offset&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;tag&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mh"&gt;0xFFFE&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="c1"&gt;# the real tag is the subformat
&lt;/span&gt;                &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unpack_from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;H&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;chunk_id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;no data chunk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things to keep. The &lt;code&gt;size % 2&lt;/code&gt; padding is required by RIFF and easy to miss, because it only matters on odd-sized chunks. Resolving &lt;code&gt;0xFFFE&lt;/code&gt; to its subformat means extensible files stop being a special case.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the naive slice actually does
&lt;/h2&gt;

&lt;p&gt;A reader ran the files and worked out something I left out: the two bugs fail very differently, and that is why the first one survives.&lt;/p&gt;

&lt;p&gt;On the 24-bit file, &lt;code&gt;data[44:]&lt;/code&gt; starts 24 bytes early. That is 8 samples, or 4 frames, so the audio is just delayed. On the &lt;code&gt;LIST&lt;/code&gt; file it starts 50 bytes early, which is 25 samples. Every sample is still there, offset by 12.5 frames, and because 25 is odd in an interleaved stream the left and right channels swap too. Both still sound like audio.&lt;/p&gt;

&lt;p&gt;The float file is the one that fails loudly. 14 bytes is 3.5 samples, so every value is spliced from halves of two neighbours. The first two come out as 4.8e-36 and 2.6e+20, and more than half of the first 20,000 land outside [-1, 1].&lt;/p&gt;

&lt;p&gt;So the offset bug is silent and the format bug is the one that crashes. Test only with integer PCM and you keep the silent one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The files
&lt;/h2&gt;

&lt;p&gt;The links in the table are direct, with no sign-up and no redirect, so curl and CI can fetch them. The &lt;a href="https://loremfile.dev/wav" rel="noopener noreferrer"&gt;full WAV set&lt;/a&gt; is on one page with a SHA-256 for each file. Everything is CC0, so you can copy them into your own fixtures instead of depending on my domain.&lt;/p&gt;

&lt;p&gt;If you have a WAV reader, point it at the 58-byte and 94-byte files and see what comes out.&lt;/p&gt;

</description>
      <category>audio</category>
      <category>python</category>
      <category>testing</category>
      <category>programming</category>
    </item>
    <item>
      <title>Sample files you can hotlink: 228 CC0 test fixtures with stable URLs</title>
      <dc:creator>Prabhash</dc:creator>
      <pubDate>Thu, 24 Sep 2026 10:11:29 +0000</pubDate>
      <link>https://dev.to/prabhash_me/sample-files-you-can-hotlink-228-cc0-test-fixtures-with-stable-urls-51gl</link>
      <guid>https://dev.to/prabhash_me/sample-files-you-can-hotlink-228-cc0-test-fixtures-with-stable-urls-51gl</guid>
      <description>&lt;p&gt;Every test suite that touches uploads eventually needs a real PDF, a video that seeks, a CSV with a broken row somewhere in it. Usually someone hotlinks a file from a random site, and a few months later it moves or changes and the test breaks for no reason.&lt;br&gt;
&lt;a href="https://loremfile.dev" rel="noopener noreferrer"&gt;loremfile.dev&lt;/a&gt; is 228 synthetic files across 78 formats, all CC0, served with CORS and byte ranges, with a SHA-256 for every file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use it&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-O&lt;/span&gt; https://loremfile.dev/pdf/a4-3pages.pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Verify it&lt;/strong&gt; (bytes at a published URL don't change):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;
&lt;span class="n"&gt;manifest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://loremfile.dev/manifest.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fixtures&lt;/span&gt;&lt;span class="sh"&gt;"&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;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pdf/a4-3pages.pdf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://loremfile.dev/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sha256&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or all at once: &lt;em&gt;sha256sum -c sha256sums.txt --ignore-missing&lt;/em&gt; (macOS: &lt;em&gt;shasum -a 256 -c sha256sums.txt --ignore-missing&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Edge cases, on purpose&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;&lt;a href="https://loremfile.dev/edge/pdf-truncated-60pct.pdf" rel="noopener noreferrer"&gt;edge/pdf-truncated-60pct.pdf&lt;/a&gt;, &lt;a href="https://loremfile.dev/edge/jpg-truncated-50pct.jpg" rel="noopener noreferrer"&gt;edge/jpg-truncated-50pct.jpg&lt;/a&gt;, &lt;a href="https://loremfile.dev/edge/mp4-truncated-50pct.mp4" rel="noopener noreferrer"&gt;edge/mp4-truncated-50pct.mp4&lt;/a&gt;, &lt;a href="https://loremfile.dev/edge/zero-byte.csv" rel="noopener noreferrer"&gt;edge/zero-byte.csv&lt;/a&gt;, &lt;a href="https://loremfile.dev/edge/json-bom.json" rel="noopener noreferrer"&gt;edge/json-bom.json&lt;/a&gt;, &lt;a href="https://loremfile.dev/edge/json-trailing-comma.json" rel="noopener noreferrer"&gt;edge/json-trailing-comma.json&lt;/a&gt;, &lt;a href="https://loremfile.dev/edge/csv-ragged-rows.csv" rel="noopener noreferrer"&gt;edge/csv-ragged-rows.csv&lt;/a&gt;&lt;/em&gt; - 19 in total, for when you want to test that a parser fails.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One Limitation&lt;/strong&gt;:&lt;br&gt;
Around 30 requests/second per IP, 429 above that. A normal test suite should be fine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use it in GitHub Actions&lt;/strong&gt;&lt;br&gt;
Also supports direct integration with Github Actions. Every file gets checked against its hash, and the step fails if a byte's off:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kumarprabhashanand/loremfile/action@action-v1&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pdf/minimal.pdf mp4/720p-5s.mp4&lt;/span&gt;
    &lt;span class="na"&gt;formats&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;svg&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;How it's built&lt;/strong&gt;&lt;br&gt;
Everything's generated from synthetic data by open-source code (MIT) in a pinned container, pushed to Cloudflare R2, and archived as a GitHub release so anyone can mirror it. No accounts, cookies, or analytics. Claude agents did most of the design and implementation here.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://loremfile.dev" rel="noopener noreferrer"&gt;https://loremfile.dev&lt;/a&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>testing</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
