<?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: David Miller</title>
    <description>The latest articles on DEV Community by David Miller (@morphio).</description>
    <link>https://dev.to/morphio</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%2F4179400%2F09c81e5a-b04c-4ad5-bc95-3aaba5e95a9f.png</url>
      <title>DEV Community: David Miller</title>
      <link>https://dev.to/morphio</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/morphio"/>
    <language>en</language>
    <item>
      <title>Do not treat image complete as a successful decode result</title>
      <dc:creator>David Miller</dc:creator>
      <pubDate>Mon, 02 Nov 2026 00:32:15 +0000</pubDate>
      <link>https://dev.to/morphio/do-not-treat-image-complete-as-a-successful-decode-result-23pf</link>
      <guid>https://dev.to/morphio/do-not-treat-image-complete-as-a-successful-decode-result-23pf</guid>
      <description>&lt;p&gt;&lt;code&gt;HTMLImageElement.complete&lt;/code&gt; can be &lt;code&gt;true&lt;/code&gt; for a broken image. It can also be true when the element has no source, so it cannot establish that an image loaded successfully.&lt;/p&gt;

&lt;p&gt;For an image-processing step, handle the operation you actually need. If the next step requires decoding, await &lt;code&gt;decode()&lt;/code&gt; and handle rejection before replacing the preview. Keep the selected job identifier available when your application allows another file to be chosen during that wait.&lt;/p&gt;

&lt;p&gt;The following is an illustrative boundary around an existing image element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;requireDecodedImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;image&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;image&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;Handle rejection in the caller. A rejected promise should leave the previous preview intact when that is the intended interface behavior. Do not label an export ready merely because an earlier &lt;code&gt;complete&lt;/code&gt; check returned true.&lt;/p&gt;

&lt;p&gt;Keep load or decode status separate from the later canvas drawing and export result. Successful decoding does not establish that every subsequent operation will succeed. Name the failing stage in the interface so a missing image and a failed export are distinguishable.&lt;/p&gt;

&lt;p&gt;Reference: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/complete" rel="noopener noreferrer"&gt;MDN complete&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>html</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Label File.lastModified as a file timestamp in an image list</title>
      <dc:creator>David Miller</dc:creator>
      <pubDate>Sun, 01 Nov 2026 02:43:29 +0000</pubDate>
      <link>https://dev.to/morphio/label-filelastmodified-as-a-file-timestamp-in-an-image-list-3c3d</link>
      <guid>https://dev.to/morphio/label-filelastmodified-as-a-file-timestamp-in-an-image-list-3c3d</guid>
      <description>&lt;p&gt;Keep the label for &lt;code&gt;File.lastModified&lt;/code&gt; specific: it is a file modification timestamp. A photograph's capture time needs a separate source.&lt;/p&gt;

&lt;p&gt;The value is a number of milliseconds since the Unix epoch. You can format it with &lt;code&gt;new Date(file.lastModified).toISOString()&lt;/code&gt; when a UTC display suits the interface. Keep the numeric value in the job record if later comparisons depend on it.&lt;/p&gt;

&lt;p&gt;A file created in JavaScript can receive an explicit &lt;code&gt;lastModified&lt;/code&gt; option. When that option is omitted, it is normally assigned the current time at creation; this makes it unsuitable as proof of when the image was photographed. A file without a known modification date can also report the current date.&lt;/p&gt;

&lt;p&gt;Keep any capture date under a separate field with its own source and uncertainty. Leave a missing capture date empty instead of substituting the file timestamp. If the user is comparing two exports, identify which timestamp was inspected and which file it belongs to.&lt;/p&gt;

&lt;p&gt;I work on &lt;a href="https://morphio.online/" rel="noopener noreferrer"&gt;Morphio&lt;/a&gt; and write about browser image workflows. This distinction concerns the file record and does not claim that a conversion tool preserves or reads camera metadata. Reference: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/File/lastModified" rel="noopener noreferrer"&gt;MDN File.lastModified&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
    </item>
    <item>
      <title>Let the browser set the multipart boundary for an image upload</title>
      <dc:creator>David Miller</dc:creator>
      <pubDate>Sat, 31 Oct 2026 07:41:15 +0000</pubDate>
      <link>https://dev.to/morphio/let-the-browser-set-the-multipart-boundary-for-an-image-upload-444f</link>
      <guid>https://dev.to/morphio/let-the-browser-set-the-multipart-boundary-for-an-image-upload-444f</guid>
      <description>&lt;p&gt;Leave &lt;code&gt;Content-Type&lt;/code&gt; unset in the request options when &lt;code&gt;fetch()&lt;/code&gt; sends &lt;code&gt;FormData&lt;/code&gt;. The browser adds the multipart boundary needed by that body.&lt;/p&gt;

&lt;p&gt;Build the form with &lt;code&gt;const data = new FormData(); data.append("image", file);&lt;/code&gt; and pass it as the request body. A manually supplied &lt;code&gt;Content-Type: multipart/form-data&lt;/code&gt; omits the boundary that the browser normally adds. Apply this rule to requests whose body is &lt;code&gt;FormData&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Check a shared request helper for an unconditional JSON or multipart header before blaming the image bytes. Keep any authorization headers your application actually requires. Inspect the request and the server's response using a small non-sensitive test image.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest_API/Using_FormData_Objects" rel="noopener noreferrer"&gt;MDN FormData guide&lt;/a&gt; explains the boundary requirement. In a bug report, identify the body type and header construction without copying credentials into the report.&lt;/p&gt;

&lt;p&gt;I work on Morphio, our image project at &lt;a href="https://morphio.online/" rel="noopener noreferrer"&gt;https://morphio.online/&lt;/a&gt;. These notes describe image preparation and review; they do not require a particular editing tool.&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Keep the filename separate when appending a Blob to FormData</title>
      <dc:creator>David Miller</dc:creator>
      <pubDate>Fri, 30 Oct 2026 03:47:49 +0000</pubDate>
      <link>https://dev.to/morphio/keep-the-filename-separate-when-appending-a-blob-to-formdata-5hj7</link>
      <guid>https://dev.to/morphio/keep-the-filename-separate-when-appending-a-blob-to-formdata-5hj7</guid>
      <description>&lt;p&gt;Keep a Blob's uploaded filename explicit in FormData. A plain Blob has no File name, so supply the name as append()'s third argument. The Blob contains data and a type, but has no File name.&lt;/p&gt;

&lt;p&gt;The third argument to append() supplies the name reported to the server. For example, form.append("image", blob, "preview.png") names the uploaded part. The default name for a plain Blob is "blob"; a File uses its own filename unless another is supplied.&lt;/p&gt;

&lt;p&gt;Choose the extension from the output format you actually produced. Giving the part a .png name does not convert its bytes. When reading the server's upload report, check the field name image and filename preview.png separately.&lt;/p&gt;

&lt;p&gt;Test with a small sample and inspect the name the server receives. Keep server-side validation in place; a supplied filename is descriptive metadata.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/FormData/append" rel="noopener noreferrer"&gt;MDN FormData.append()&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
    </item>
    <item>
      <title>Treat a file input accept attribute as a chooser hint</title>
      <dc:creator>David Miller</dc:creator>
      <pubDate>Thu, 29 Oct 2026 02:33:47 +0000</pubDate>
      <link>https://dev.to/morphio/treat-a-file-input-accept-attribute-as-a-chooser-hint-354d</link>
      <guid>https://dev.to/morphio/treat-a-file-input-accept-attribute-as-a-chooser-hint-354d</guid>
      <description>&lt;p&gt;An image file input still needs validation when it has an accept attribute. The attribute supplies a picker hint; it does not verify the chosen file.&lt;/p&gt;

&lt;p&gt;For an image form, accept="image/*" can make selection easier, while the application still needs to check the file it receives. Keep the destination's supported formats explicit in the interface instead of treating the broad image filter as its complete policy.&lt;/p&gt;

&lt;p&gt;Validate the expected requirement on the server too. Keep server validation in place when adding a client-side selection check. If the destination rejects a file, preserve its error rather than treating the presence of an image extension as proof of acceptance.&lt;/p&gt;

&lt;p&gt;Test a supported sample and a deliberately unsuitable sample through the same form. Record the selected filename and the actual response for each attempt.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/accept" rel="noopener noreferrer"&gt;MDN accept&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I work on &lt;a href="https://morphio.online/" rel="noopener noreferrer"&gt;Morphio&lt;/a&gt;, and this note is part of the image-workflow guidance I maintain.&lt;/p&gt;

</description>
      <category>html</category>
      <category>security</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Compare image file limits in bytes before formatting the size</title>
      <dc:creator>David Miller</dc:creator>
      <pubDate>Wed, 28 Oct 2026 02:22:02 +0000</pubDate>
      <link>https://dev.to/morphio/compare-image-file-limits-in-bytes-before-formatting-the-size-2288</link>
      <guid>https://dev.to/morphio/compare-image-file-limits-in-bytes-before-formatting-the-size-2288</guid>
      <description>&lt;p&gt;Check an image file against the exact byte limit before rounding its size for display. File.size supplies the byte count used by that comparison.&lt;/p&gt;

&lt;p&gt;Keep the limit as a number in the validation code. For a hypothetical form with a stated limit of 2,000,000 bytes, the condition file.size &amp;gt; 2_000_000 rejects files above that value. Equality remains accepted under that particular rule. Use the destination's actual requirement instead of assuming what its MB label means.&lt;/p&gt;

&lt;p&gt;Format a separate value for the interface after the comparison. Keep the unit clear if you display decimal megabytes or binary mebibytes. A file that rounds to the displayed limit can still exceed the exact byte count.&lt;/p&gt;

&lt;p&gt;This check establishes only the local size comparison. The destination can reject an upload for another reason. Preserve its error message and check the accepted preview separately.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Blob/size" rel="noopener noreferrer"&gt;MDN Blob.size&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
    </item>
    <item>
      <title>Match settled image jobs to their original inputs</title>
      <dc:creator>David Miller</dc:creator>
      <pubDate>Tue, 27 Oct 2026 04:19:22 +0000</pubDate>
      <link>https://dev.to/morphio/match-settled-image-jobs-to-their-original-inputs-109o</link>
      <guid>https://dev.to/morphio/match-settled-image-jobs-to-their-original-inputs-109o</guid>
      <description>&lt;p&gt;Pair Promise.allSettled() outcomes with a captured copy of the input list. The records keep input order even when an image job started earlier finishes later.&lt;/p&gt;

&lt;p&gt;Use the status field before reading a result. A fulfilled record has a value; a rejected record has a reason. Keep the corresponding source identifier with either kind of record so a failed image job is not silently removed from the batch report.&lt;/p&gt;

&lt;p&gt;The returned order does not impose a concurrency limit or establish that a produced image was downloaded by the user. Those are separate parts of the workflow. If the interface permits another selection while work runs, keep the running batch's list unchanged.&lt;/p&gt;

&lt;p&gt;Before showing the summary, compare the number of outcome records with the captured input list. Display failures alongside successful jobs, with wording that describes what the application actually confirmed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled" rel="noopener noreferrer"&gt;Reference: MDN Promise.allSettled()&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Published by &lt;a href="https://morphio.online/" rel="noopener noreferrer"&gt;Morphio&lt;/a&gt; as part of our practical image workflow notes.&lt;/p&gt;

</description>
      <category>javascript</category>
    </item>
    <item>
      <title>Release an image preview object URL when its owner removes it</title>
      <dc:creator>David Miller</dc:creator>
      <pubDate>Mon, 26 Oct 2026 00:34:39 +0000</pubDate>
      <link>https://dev.to/morphio/release-an-image-preview-object-url-when-its-owner-removes-it-3n69</link>
      <guid>https://dev.to/morphio/release-an-image-preview-object-url-when-its-owner-removes-it-3n69</guid>
      <description>&lt;p&gt;Keep each object URL with the image preview that owns it. Release the URL when that preview is removed and its consumers no longer need it.&lt;/p&gt;

&lt;p&gt;Calling URL.revokeObjectURL() releases the object URL created by URL.createObjectURL(). Revoking a URL as soon as it is assigned can be too early if user interactions still need it.&lt;/p&gt;

&lt;p&gt;When replacing a preview, give the new preview its own URL and retire the previous one according to the same ownership rule. Include cleanup when the containing view is closed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/URL/revokeObjectURL_static" rel="noopener noreferrer"&gt;Reference: MDN revokeObjectURL()&lt;/a&gt;&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>javascript</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Give same-named image files separate batch IDs</title>
      <dc:creator>David Miller</dc:creator>
      <pubDate>Sun, 25 Oct 2026 04:37:25 +0000</pubDate>
      <link>https://dev.to/morphio/give-same-named-image-files-separate-batch-ids-31gj</link>
      <guid>https://dev.to/morphio/give-same-named-image-files-separate-batch-ids-31gj</guid>
      <description>&lt;p&gt;Assign image-batch rows identifiers that do not depend on File.name being unique. MDN documents that name excludes the path. A name does not establish that two selected files are the same file.&lt;/p&gt;

&lt;p&gt;Assign the row IDs when accepting the batch. Display the original filename separately.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;makeBatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;batchId&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="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;batchId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pending&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The caller must supply a batchId that is unique among batches retained by the application. The index distinguishes rows within that batch. It is not a content hash and does not deduplicate files.&lt;/p&gt;

&lt;p&gt;Use the row ID to attach each result or error to its input. Reordering the visible list should not change the ID assigned when the row was created.&lt;/p&gt;

&lt;p&gt;I work on &lt;a href="https://morphio.online/" rel="noopener noreferrer"&gt;Morphio&lt;/a&gt;, an image conversion project. The bookkeeping pattern here is a design suggestion. It does not describe the project's implementation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/File/name" rel="noopener noreferrer"&gt;Reference: MDN File.name&lt;/a&gt;&lt;/p&gt;

</description>
      <category>coding</category>
      <category>javascript</category>
      <category>programming</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Reject invalid output sizes before assigning canvas.width</title>
      <dc:creator>David Miller</dc:creator>
      <pubDate>Sat, 24 Oct 2026 03:33:56 +0000</pubDate>
      <link>https://dev.to/morphio/reject-invalid-output-sizes-before-assigning-canvaswidth-3p4</link>
      <guid>https://dev.to/morphio/reject-invalid-output-sizes-before-assigning-canvaswidth-3p4</guid>
      <description>&lt;p&gt;Validate the requested dimensions before changing a canvas. Decide which sizes your application accepts, and report an invalid request before it changes the preview.&lt;/p&gt;

&lt;p&gt;This example accepts numbers and numeric strings. After conversion, each side must be an integer from 1 to 4096. The cap is a sample application policy, not a portable limit for browsers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;checkedSide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&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;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&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="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isSafeInteger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RangeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Output side must be an integer from 1 to 4096&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;makeOutputCanvas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;height&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;w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;checkedSide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;width&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;h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;checkedSide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;height&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;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;canvas&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;h&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;canvas&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;The function checks both inputs before creating an output canvas. Passing an empty string fails because it converts to zero. Passing "512" succeeds. If your form requires a stricter text format, replace that conversion rule with the parsing policy you intend to support.&lt;/p&gt;

&lt;p&gt;Creating a separate output canvas leaves the existing preview alone. Setting the width of an existing canvas clears its buffer and resets the drawing context, even if you assign its current width again.&lt;/p&gt;

&lt;p&gt;A valid request can still fail later. Choose limits with the total pixel count and concurrent work in mind, and handle errors when you obtain a context or export the result. The integer check alone does not establish that the browser can allocate the image.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/width" rel="noopener noreferrer"&gt;Reference: MDN canvas.width&lt;/a&gt;&lt;/p&gt;

</description>
      <category>coding</category>
      <category>javascript</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Calculate drawImage crops from the source image dimensions</title>
      <dc:creator>David Miller</dc:creator>
      <pubDate>Fri, 23 Oct 2026 03:18:29 +0000</pubDate>
      <link>https://dev.to/morphio/calculate-drawimage-crops-from-the-source-image-dimensions-472d</link>
      <guid>https://dev.to/morphio/calculate-drawimage-crops-from-the-source-image-dimensions-472d</guid>
      <description>&lt;p&gt;A crop rectangle passed to &lt;code&gt;drawImage()&lt;/code&gt; belongs to the source image. Calculate it from the intrinsic image dimensions, including when CSS makes the preview smaller.&lt;/p&gt;

&lt;p&gt;Suppose a decoded image has intrinsic dimensions of 1600 by 1200, and its preview is 400 by 300. A source crop 200 units wide covers one eighth of the image width. A 200-unit selection in the preview covers half its width. &lt;br&gt;
Use &lt;code&gt;naturalWidth&lt;/code&gt; and &lt;code&gt;naturalHeight&lt;/code&gt; when calculating a centered square for an &lt;code&gt;HTMLImageElement&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;drawSquare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;outputSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;256&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;side&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;naturalWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;naturalHeight&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;side&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&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;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Image has no usable dimensions&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;sx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;naturalWidth&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;side&lt;/span&gt;&lt;span class="p"&gt;)&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;naturalHeight&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;side&lt;/span&gt;&lt;span class="p"&gt;)&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="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;side&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;side&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;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;outputSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;outputSize&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;Decode the image before calling this function. This example assumes a correctly sized destination canvas, an identity drawing transform, and a positive output size. It does not translate a pointer selection from the preview.&lt;/p&gt;

&lt;p&gt;For an interactive crop, first convert the selection from the displayed image coordinates into source coordinates. Account for any space around the displayed photo before applying the width and height ratios.&lt;/p&gt;

&lt;p&gt;I work on &lt;a href="https://morphio.online/" rel="noopener noreferrer"&gt;Morphio&lt;/a&gt;. In image-workflow notes, I record source coordinates and output dimensions separately so someone can check each calculation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage" rel="noopener noreferrer"&gt;Reference: MDN drawImage()&lt;/a&gt;&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>html</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Map pointer coordinates into the canvas drawing buffer</title>
      <dc:creator>David Miller</dc:creator>
      <pubDate>Thu, 22 Oct 2026 01:59:09 +0000</pubDate>
      <link>https://dev.to/morphio/map-pointer-coordinates-into-the-canvas-drawing-buffer-45gn</link>
      <guid>https://dev.to/morphio/map-pointer-coordinates-into-the-canvas-drawing-buffer-45gn</guid>
      <description>&lt;p&gt;Convert the pointer position into drawing-buffer coordinates before drawing a marker on a resized canvas. The event reports viewport coordinates, while the drawing buffer has its own width and height.&lt;/p&gt;

&lt;p&gt;For a canvas displayed at half its buffer width, one CSS pixel covers two buffer pixels. Subtracting the canvas position gives a local CSS coordinate. Multiply that value by the buffer-to-display ratio before drawing a marker or choosing a crop boundary.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;canvasPoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&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;rect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBoundingClientRect&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;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&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;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientX&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientY&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&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;This example assumes an axis-aligned canvas with no CSS border or padding and no rotation or skew. Put a decorative border on a wrapper. The bounding rectangle includes border and padding, so those would need separate handling if placed on the canvas itself.&lt;/p&gt;

&lt;p&gt;The returned coordinates describe the canvas buffer. If the drawing context has a transform, or if the photo is letterboxed inside that buffer, mapping into the photo requires another step. Reject clicks outside the displayed photo before calculating a crop.&lt;/p&gt;

&lt;p&gt;Check the mapping near both corners after resizing the page. If a marker follows the pointer near the upper-left corner but drifts near the lower-right, check the scale conversion. Read the current rectangle for the interaction; a rectangle saved before a layout change can be stale.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect" rel="noopener noreferrer"&gt;Reference: MDN&lt;/a&gt;&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
