<?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: Jen Looper</title>
    <description>The latest articles on DEV Community by Jen Looper (@jenlooper).</description>
    <link>https://dev.to/jenlooper</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%2F9819%2Fd0d0dc5d-031b-43fe-85a4-469def492123.jpg</url>
      <title>DEV Community: Jen Looper</title>
      <link>https://dev.to/jenlooper</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jenlooper"/>
    <language>en</language>
    <item>
      <title>Calavera Cabina: a tribute app for Día de Muertos</title>
      <dc:creator>Jen Looper</dc:creator>
      <pubDate>Mon, 26 Oct 2026 21:22:25 +0000</pubDate>
      <link>https://dev.to/cloudinary/calavera-cabina-a-tribute-app-for-dia-de-muertos-1bko</link>
      <guid>https://dev.to/cloudinary/calavera-cabina-a-tribute-app-for-dia-de-muertos-1bko</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week4-2026-10-26"&gt;Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The booth that didn't happen
&lt;/h2&gt;

&lt;p&gt;Picture this: it's spooky season, late October, and you are building a booth for a conference. People are getting creative: on the left, someone has a tarot card activity with a crystal ball and are saging your codebases against bugs. On the right, folks are handing out Halloween candy with the phrase, "You're worth a mint to us". &lt;/p&gt;

&lt;p&gt;I wanted to build a seasonal experience that would lean into Cloudinary's new image generation tooling: people walk up, take a selfie, and leave with a generated sticker. The catch: I only have a thermal printer on hand, but what about creating something in black and white, like a tatoo? What about printing a stylized selfie that reflects an interesting seasonal image?&lt;/p&gt;

&lt;p&gt;What about Día de Muertos? Mexico's famous holiday full of rich symbolism and imagery such as colorful, fashionable skeletons, cut paper garlands, brilliant florals, and more.&lt;/p&gt;

&lt;p&gt;Because of some logistic snafus, the seasonal booth experience for conference didn't materialize. The app is built, however, so I wanted to share it, taking you along with me down the rabbit hole into scary stories and legends.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqf20d2zc87xesku5rel9.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqf20d2zc87xesku5rel9.png" alt="colorful image prototype" width="800" height="842"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A colorful prototype&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why Día de Muertos
&lt;/h2&gt;

&lt;p&gt;If you're on "spookytok" - TikTok's Halloween stream, you'll soon hear a beautiful song, "La Llorona" - the weeping woman. Depending on the interpretation, she is a woman who cries for her lost children or her lost love, wandering by waterways (something like the Irish Banshee), wailing. Often the song is paired with images of the  "Calavera Catrina", a stylish skeleton lady (more below). Could we create a transformation of a selfie into La Llorona? &lt;/p&gt;

&lt;p&gt;An authentic image of the Wailing Lady is more like a tragic figure than a saucy skeleton:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkxfxl071yc2qaxnyhhif.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkxfxl071yc2qaxnyhhif.png" alt="La Lloraona" width="800" height="896"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Me as La Llorona, not a cheerful image&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I hesitated. Is it too sad an image? We want people to view themselves as taking part in a festive event, not a funeral! And moreover, for the men coming to the booth, how would we visualize them? Is there a wailing banshee equivalent? &lt;/p&gt;

&lt;p&gt;This took me down quite a journey away from Mexico and into Venezuela, where we encounter &lt;a href="https://en.wikipedia.org/wiki/El_Silb%C3%B3n" rel="noopener noreferrer"&gt;El Silbón&lt;/a&gt;, "The Whistler", a lost soul carrying a bag on his back, whistling a warning of imminent doom to passersby. It's a really scary story, far too much for the unwary folks who would wander to our booth!&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgi53nrs2hln223qtdmvl.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgi53nrs2hln223qtdmvl.png" alt="El Silbon" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;My long-suffering husband as El Silbon&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It was time to return to the brighter aesthetic of Día de Muertos. The imagery that works best is the true Catrina or Catrin - the familiar fashionable skeleton celebrated with face painting and costumes. Catrina was popularized in the early 1900s by Mexican artist Jose Guadalupe Posada: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5vx20hzqltqtu8izyyno.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5vx20hzqltqtu8izyyno.webp" alt="Posada illustration" width="800" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;La Calavera Catrina, c. 1910, lithograph&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;She and her dandy skeleton counterpart, El Catrín, poke sly fun at the Europeanized gentry of the time, while also drawing on a the much older traditions of the Aztec queen of the underworld,  Mictēcacihuātl, mixed with Catholic imagery around All Saint's Day. &lt;a href="https://thegracemuseum.org/learn/2020-11-2-la-catrina-a-history/" rel="noopener noreferrer"&gt;source&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmlr4vizmm1xkl92s2q9i.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmlr4vizmm1xkl92s2q9i.png" alt="Llorona" width="800" height="1215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;On TikTok, we have ladies dressed in their finest Catrina garb paired with the haunting Llorona song...an interestingly mixed metaphor.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It took me a little while to get the aesthetic where I wanted it: spooky, but not scary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Now, for the prompt!
&lt;/h2&gt;

&lt;p&gt;With AI, it's trivially easy to scaffold an app that will take your selfie and use Cloudinary's Image Generation tooling to generate something that would work for my little thermal printer. The trick is to get the prompt right. I experimented several times to find something that would print well, produce a recognizable portrait, and be true to the aesthetic of the Catrina:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  "Restyle the person in [1] as a Día de Muertos-inspired fine-line tattoo portrait. " +
  "Preserve their identity, facial features, hairstyle, proportions, and recognizable expression. " +
  "Use crisp black ink on a pure white background with delicate tattoo-flash linework, " +
  "clean contours, restrained stippling, strong negative space, and clear details that print well at small size. " +
  "Use dignified ceremonial clothing inspired by traditional Mexican portraiture: an elegant embroidered blouse, " +
  "dress, rebozo, lace-trimmed neckline, or formal charro-inspired garment. " +
  "Avoid modern casual clothing, business attire, exposed cleavage, costume-like clothing, or generic fantasy garments. " +
  "Keep the face human and recognizable rather than replacing it with a skull. " +
  "No color, grayscale, gradients, painterly shading, beige texture, photorealism, text, or watermark.";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, in this very simple Astro app, send that prompt along with the image to the Image Generation API:&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="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;stylePrompts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt; &lt;span class="kd"&gt;as &lt;/span&gt;&lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;stylePrompts&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="na"&gt;reference_images&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;source_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;managed_asset&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;asset_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;asset_id&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
                &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;preference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;quality&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="na"&gt;image_size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;aspect_ratio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3:4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;resolution&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1K&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;target_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;managed_asset&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;public_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`calavera-cabina/portraits/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&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="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 use of 'mode: 'auto' asks Cloudinary to choose thea model that will work best for your prompt. Depending on your model, you might get a different result, so it's important to have a good prompt (AI can help with crafting one):&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fczl4pgsgz5q2yyhn72rj.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fczl4pgsgz5q2yyhn72rj.png" alt="example of a sticker" width="800" height="1071"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One model kept building interesting frames and using spiderweb images for some reason, so I had to rework the prompt&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Pick your model
&lt;/h2&gt;

&lt;p&gt;You can specify that you want to use open weight models with this system as well, including flux-2-klein-9b-edit and qwen-image-3-edit. Just change the model from 'auto' to a given model: &lt;code&gt;model: { id: 'an-open-weight-model' }&lt;/code&gt;. Flux gives the following image, which labeled as more photorealistic (so aligns less with your prompt):&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqf27pm05cjn3kcqwbfw8.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqf27pm05cjn3kcqwbfw8.png" alt="La Catrina" width="768" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;More stylized and more dramatic&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;or Qwen:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fffrd0z3c14hddnh9sh18.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fffrd0z3c14hddnh9sh18.png" alt="La Catrina" width="768" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This took longer to draw but is arguably the nicest representation!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Building the Calavera Cabina
&lt;/h2&gt;

&lt;p&gt;I can't take credit for the clever pun (thanks, Lovable!) - but the Calavera Cabina (Skeleton Camera, as opposed to the Calavera Catrina), was starting to take shape.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsayfb79xoymnhgjpk1lr.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsayfb79xoymnhgjpk1lr.png" alt="home page" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Home screen of the Cabina&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I decided to give users a choice. Let them take a selfie and choose a style, leaning on imagery of the Catrin/Catrina, or a floral motif style, or a darker, mystical night style reflecting the Llorona mood. They all turned out beautifully!&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq9w74kadil5eh9g232dn.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq9w74kadil5eh9g232dn.png" alt="final result" width="800" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The final result&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;We are dealing with layers of cultural context in this app experience: the pre-Colombian, pre-Christian experience of early Central America, the Colonial era's interpretation of these images, and modern layering of cultural context to create a true Catrina. While the weight files for models like flux-2-klein-9b and qwen-image-3 are publicly downloadable, their creators (Black Forest Labs and Alibaba Qwen) do not publish the specific training datasets used to train them from scratch. Still, we can get some insight into high-level summaries of their sources, and perhaps feel a little reassured that some aspets of the deep cultural human context has been respected when building image generation tooling. World culture deserves context open to world learners.&lt;/p&gt;

&lt;h2&gt;
  
  
  An invitation
&lt;/h2&gt;

&lt;p&gt;I want to be extremely respectful of cultural norms and local holidays, and I know I'm approaching this as an outsider. From a self-education standpoint, I was really happy that my meanderings with this app concept led me into new sources of knowledge. It's fascinating to me to find parallels between the stories of Celtic water-wanderers I grew up with and the crying lady of Mexico. But the Día de Muerto's particular aesthetic seems to stand out as a delightfully spooky, deliciously fun celebration of deep culture. &lt;/p&gt;

&lt;p&gt;I invite you to check out the codebase and fork it to try the app yourself. You'll need an API Key and Secret alongside your Cloud Name, an upload preset for your images, and the Image Generation Add-On: &lt;a href="https://github.com/cloudinary-devs/photo-booth" rel="noopener noreferrer"&gt;https://github.com/cloudinary-devs/photo-booth&lt;/a&gt;. Print yourself a tatoo-style sticker! And Happy Halloween, Día de Muertos, and All Saint's Day!&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgayv5jxb7o28o8ji3g2l.jpeg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgayv5jxb7o28o8ji3g2l.jpeg" alt="printing the sticker" width="320" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The printed sticker result&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Cloudinary ❤️ developers&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ready to level up your media workflow? Start using Cloudinary for free and build better visual experiences today.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👉 &lt;strong&gt;&lt;a href="https://link.cloudinary.com/ut6my" rel="noopener noreferrer"&gt;Create your free account&lt;/a&gt;&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Shout-out to the folks I consulted with on this article: Diego from Colombia 🇨🇴 and Anthar from Mexico 🇲🇽 💕&lt;/p&gt;

</description>
      <category>ai</category>
      <category>beginners</category>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>"I think that I shall never see a poem lovely as a tree": A Fall Foliage App Build</title>
      <dc:creator>Jen Looper</dc:creator>
      <pubDate>Thu, 22 Oct 2026 16:42:59 +0000</pubDate>
      <link>https://dev.to/cloudinary/i-think-that-i-shall-never-see-a-poem-lovely-as-a-tree-a-fall-foliage-app-build-11fl</link>
      <guid>https://dev.to/cloudinary/i-think-that-i-shall-never-see-a-poem-lovely-as-a-tree-a-fall-foliage-app-build-11fl</guid>
      <description>&lt;p&gt;Sometimes you need to touch grass. Other times, you want to commemorate the beautiful Fall foliage that you peep outside your window and create a crowdsourced way for other folks to share their trees too! This app celebrates the second kind of day.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://local-leaf.netlify.app" rel="noopener noreferrer"&gt;Local Leaf&lt;/a&gt; is an Astro app that ranks neighborhood tree photos by fall color. It uses Cloudinary for moderated uploads, tags, and predominant-color detection, and this post shows how to build it. The source code is on &lt;a href="https://github.com/jlooper/foliage" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;In the tradition of creating somewhat pointless - but highly aesthetic - software, you too can build an app to celebrate nature.&lt;/p&gt;

&lt;h3&gt;
  
  
  What you'll build
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;How to add the Cloudinary upload widget to an Astro app&lt;/li&gt;
&lt;li&gt;How to set up an upload preset that sends photos to a moderation queue&lt;/li&gt;
&lt;li&gt;How to store simple data as Cloudinary tags and context metadata instead of a database&lt;/li&gt;
&lt;li&gt;How to read Cloudinary’s predominant-color data for each photo&lt;/li&gt;
&lt;li&gt;How to turn that color data into leaderboard scores for yellow, orange, and crimson trees&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Leaf Peeping Needs a Leaderboard
&lt;/h2&gt;

&lt;p&gt;Everywhere in New England there are leaf-peeping forays at this time of year. People drive for hours to surround themselves in forest colors, driving to Western Massachusetts, Vermont, and New Hampshire to look at the deciduous trees as they change colors in the Fall. Depending on the summer and fall weather patterns, colors can vary dramatically from fire-engine red, to soft oranges, bright yellows, or simple browns. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Leaf peeping is a real thing, and a big tourist draw. Check out &lt;a href="https://leafpeepers.com/wpleaf/cams/" rel="noopener noreferrer"&gt;these live web cams&lt;/a&gt; showing realtime foliage.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Maybe leaf peeping could become a bit more competitive. What if you could rank locally-sourced trees by color, classifying them according to how close they are to a pure yellow, crimson, or orange hex color? Because Cloudinary gives you a way to quickly upload and moderate UGC (user-generated content) via its upload widget, analyzing color and gathering tags for each uploaded image, it seemed like a good way to signal that your neighborhood fall foliage is at its peak, right now, so come and peep!&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flukfqkwwrsu218wzhkux.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flukfqkwwrsu218wzhkux.png" alt="Local Leaf" width="799" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Easy Moderated Uploading with a Widget
&lt;/h2&gt;

&lt;p&gt;This app is built using Astro, and includes a handy upload widget, which you include by adding it to your index.astro file. This widget allows you to quickly upload batches of photos, great when you're walking and snapping pics.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://upload-widget.cloudinary.com/latest/global/all.js"&lt;/span&gt; &lt;span class="na"&gt;is:inline&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add a few data points in your form, and upload your photos using an upload preset that's set to send photos to moderation queues. Admins can approve the pics to keep the data clean.&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;openUploadWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;treeType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;neighborhood&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;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloudinary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createUploadWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;

      &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;submit&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="nx"&gt;event&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="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&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;form&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;submitButton&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;treeTypeInput&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;neighborhoodInput&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="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;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reportValidity&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="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;cloudName&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;uploadPreset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Image uploads are not configured yet.&lt;/span&gt;&lt;span class="dl"&gt;'&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="p"&gt;}&lt;/span&gt;

        &lt;span class="nx"&gt;uploadedCount&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="nx"&gt;failedCount&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="nx"&gt;pendingFinalizations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
        &lt;span class="nx"&gt;submitButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;submitButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Opening photo picker…&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&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;widget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;openUploadWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;treeTypeInput&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="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;neighborhoodInput&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="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="nx"&gt;widget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&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 upload widget looks like this:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq0g4vn11ajvft03gsyv0.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq0g4vn11ajvft03gsyv0.png" alt="uploading" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Those photos are fed to your upload preset, a prerequisite for an app built for image uploading. Set your upload preset, which functions as a 'landing slot' for your images in your account, to support moderation: &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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7aejge41s0ip44d2iic5.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7aejge41s0ip44d2iic5.png" alt="moderation" width="800" height="590"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then you can quickly set your images to go live by approving them in the moderation workflow:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg63fsblmjrj4w8pozwgp.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg63fsblmjrj4w8pozwgp.png" alt="moderating images" width="800" height="661"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tags as Metadata
&lt;/h2&gt;

&lt;p&gt;The images are uploaded via an upload preset that dumps all images into a /foliage folder with their tags. Leveraging tags in Cloudinary allows you to bypass the creation of a custom database, for simple data - a great technique when you want to add some context to your images for simple apps. &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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4opu3eufx3jy4o27wfvk.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4opu3eufx3jy4o27wfvk.png" alt=" " width="799" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Color Us Beautiful: Scoring Trees with Cloudinary Predominant Color Detection
&lt;/h2&gt;

&lt;p&gt;Cloudinary's predominant-color detection system is meant to power e-commerce apps so that your catalogue can be represented truthfully. But we can also use it to power our custom foliage scoring algorithm.&lt;/p&gt;

&lt;p&gt;Each photo's palette comes back from Cloudinary as a list of hex colors found in a photo. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;Rubric.mjs&lt;/code&gt; turns Cloudinary's predominant-color data into leaderboard scores. It has four parts.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;TARGETS defines the three leaderboards:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each has a label, a display hex, and a list of Cloudinary color buckets it counts.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;serializePredominant(predominant)&lt;/code&gt; function in &lt;code&gt;finalize-upload.ts&lt;/code&gt; is used at upload time, turning Cloudinary's [['red', 50], ['pink', 9.8], ...] into a compact string like red:50.0,pink:9.8, which is stored in the asset's context metadata.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;parsePredominant(value)&lt;/code&gt; function in &lt;code&gt;rubric.mjs&lt;/code&gt; is used when the gallery loads, in &lt;code&gt;foliage.ts&lt;/code&gt;. It reverses the serialization and returns [] for missing or malformed values.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;scorePredominant(predominant, targetKey)&lt;/code&gt; function in &lt;code&gt;rubric.mjs&lt;/code&gt; does the scoring.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fddvrp2j7d7s4of5yampk.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fddvrp2j7d7s4of5yampk.png" alt="ranked trees" width="799" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Try Local Leaf and Upload Your Own Foliage
&lt;/h2&gt;

&lt;p&gt;In a short time, you've built a nature-friendly app that allows all of us to admire your local foliage. I invite you to visit &lt;a href="https://local-leaf.netlify.app/" rel="noopener noreferrer"&gt;https://local-leaf.netlify.app/&lt;/a&gt; and upload your foliage! Can you beat the glorious sugar maple from Commonwealth Avenue in Newton, MA? Let's see!&lt;/p&gt;




&lt;p&gt;This app was inspired both by the glorious colors outside my window in October in New England, but also by this simple poem by Joyce Kilmer from 1913:&lt;/p&gt;

&lt;p&gt;I think that I shall never see&lt;br&gt;
A poem lovely as a tree.&lt;/p&gt;

&lt;p&gt;A tree whose hungry mouth is prest&lt;br&gt;
Against the earth's sweet flowing breast;&lt;/p&gt;

&lt;p&gt;A tree that looks at God all day,&lt;br&gt;
And lifts her leafy arms to pray;&lt;/p&gt;

&lt;p&gt;A tree that may in Summer wear&lt;br&gt;
A nest of robins in her hair;&lt;/p&gt;

&lt;p&gt;Upon whose bosom snow has lain;&lt;br&gt;
Who intimately lives with rain.&lt;/p&gt;

&lt;p&gt;Poems are made by fools like me,&lt;br&gt;
But only God can make a tree.&lt;/p&gt;




&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Cloudinary ❤️ developers&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ready to level up your media workflow? Start using Cloudinary for free and build better visual experiences today.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👉 &lt;strong&gt;&lt;a href="https://link.cloudinary.com/utYHs" rel="noopener noreferrer"&gt;Create your free account&lt;/a&gt;&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>webdev</category>
      <category>astro</category>
      <category>programming</category>
      <category>newbie</category>
    </item>
    <item>
      <title>Build a Tiny Virtual Pet on Arduino in 60 Minutes 🐾</title>
      <dc:creator>Jen Looper</dc:creator>
      <pubDate>Tue, 06 Oct 2026 00:47:44 +0000</pubDate>
      <link>https://dev.to/heraistudio/build-a-tiny-virtual-pet-on-arduino-in-60-minutes-2cf</link>
      <guid>https://dev.to/heraistudio/build-a-tiny-virtual-pet-on-arduino-in-60-minutes-2cf</guid>
      <description>&lt;p&gt;What if your first hardware project wasn't another blinking LED, but a tiny pet that gets hungry, plays with you, falls asleep, and reacts to what you type?&lt;/p&gt;

&lt;p&gt;Meet &lt;strong&gt;Kiku&lt;/strong&gt;. 🌸&lt;/p&gt;

&lt;p&gt;Kiku is the mascot and in-house pet of &lt;strong&gt;Her AI Studio&lt;/strong&gt;, a community program helping the next generation of women learn and build with AI.&lt;/p&gt;

&lt;p&gt;In this mini workshop, we're shrinking Kiku down until the entire pet can live on an &lt;strong&gt;Arduino Uno Q&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Full tutorial is available in the &lt;strong&gt;&lt;a href="https://her-ai-studio.github.io/curriculum/mini-workshop.html" rel="noopener noreferrer"&gt;Her AI Studio curriculum&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an Arduino Uno Q&lt;/li&gt;
&lt;li&gt;a USB cable&lt;/li&gt;
&lt;li&gt;your laptop&lt;/li&gt;
&lt;li&gt;Arduino App Lab&lt;/li&gt;
&lt;li&gt;about 60 minutes&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Tip! To celebrate Hacktoberfest 2026, swag packs from Major League Hacking are shipping with these Arduinos, so use this workshop for your Hack Day as an introduction to hardware hacking.&lt;/p&gt;
&lt;/blockquote&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx8t20elapr3y4ngtrrm7.jpeg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx8t20elapr3y4ngtrrm7.jpeg" alt="sleep" width="800" height="535"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Shhh, Kiku is sleeping&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By the end, you'll have a tiny Tamagotchi-style pet whose face lives on the board's LED matrix. You'll be able to &lt;strong&gt;feed it, play with it, and put it to sleep from your keyboard&lt;/strong&gt;. More importantly, you'll see how a surprisingly small amount of code can produce behavior that feels alive.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll learn
&lt;/h2&gt;

&lt;p&gt;This project brings together several useful embedded-programming concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;flashing code onto physical hardware&lt;/li&gt;
&lt;li&gt;communicating with a microcontroller over Serial&lt;/li&gt;
&lt;li&gt;drawing graphics on an LED matrix&lt;/li&gt;
&lt;li&gt;tracking application state&lt;/li&gt;
&lt;li&gt;responding to user input&lt;/li&gt;
&lt;li&gt;creating simple animations&lt;/li&gt;
&lt;li&gt;building a tiny state machine&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've mostly written software that stays inside your laptop, there's something especially satisfying about seeing your code suddenly appear on physical hardware.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 1: Make sure the board is alive
&lt;/h2&gt;

&lt;p&gt;Before building a virtual pet, start with the embedded developer's version of &lt;code&gt;"Hello, world!"&lt;/code&gt;: &lt;strong&gt;Blink an LED.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Connect your Uno Q to your laptop over USB and open Arduino App Lab which you've downloaded. Login to your board.&lt;/p&gt;

&lt;p&gt;Create a new app, and take a look at the scaffolded code. Open &lt;code&gt;sketch.ino&lt;/code&gt;, and write a basic blink sketch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LED_BUILTIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LED_BUILTIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LED_BUILTIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&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;Press &lt;code&gt;run&lt;/code&gt; in App Lab.&lt;/p&gt;

&lt;p&gt;If the built-in LED turns on and off every second, &lt;strong&gt;it's aliiiiive!&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If the board isn't detected, check your USB cable. Some USB cables provide power but don't carry data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Part 2: Give your Arduino a way to listen
&lt;/h2&gt;

&lt;p&gt;Now we need Kiku to hear us. We could add buttons or sensors (and we do, in our full workshop with our full AI Kit), but there's a much simpler interface already available: &lt;strong&gt;Serial communication&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The same USB connection used to upload your program can carry messages between your laptop and your Arduino while the program is running. In App Lab, it looks like this:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy9mfzadg4xz0eh4jn5iv.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy9mfzadg4xz0eh4jn5iv.png" alt="Serial Monitor" width="800" height="593"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Start Serial communication in &lt;code&gt;setup()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9600&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;Then check for incoming characters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&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="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;typed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&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="n"&gt;typed&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'f'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// feed Kiku&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="n"&gt;typed&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'p'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// play with Kiku&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="n"&gt;typed&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'s'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// sleep or wake up&lt;/span&gt;
    &lt;span class="p"&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;Your keyboard has effectively become the controller for a physical device.&lt;/p&gt;

&lt;p&gt;To interact with Kiku, we'll use 3 keystrokes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;f → feed
p → play
s → sleep / wake
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Part 3: Give Kiku some feelings
&lt;/h2&gt;

&lt;p&gt;A virtual pet needs more than commands. It needs &lt;strong&gt;state&lt;/strong&gt;. Kiku tracks three simple stats:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;hunger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;joy&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="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;energy&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="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;asleep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll treat each stat as a value from &lt;code&gt;0&lt;/code&gt; to &lt;code&gt;10&lt;/code&gt;. Now actions can change the pet by incrementing or decrementing these values. Feeding Kiku, for example, reduces hunger (but don't overfeed!):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;hunger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;max&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="n"&gt;hunger&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;joy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;joy&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Playing might make Kiku happier while consuming energy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;joy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;joy&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;energy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;max&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="n"&gt;energy&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="n"&gt;hunger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hunger&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Make time matter
&lt;/h2&gt;

&lt;p&gt;If Kiku's stats only changed when we typed something, the pet wouldn't feel particularly alive.&lt;/p&gt;

&lt;p&gt;So we introduce time! Every few seconds, a &lt;code&gt;tick()&lt;/code&gt; function updates Kiku's state, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;tick&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="n"&gt;asleep&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// regain energy&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// lose energy&lt;/span&gt;
    &lt;span class="c1"&gt;// gradually lose joy&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// gradually become hungry&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your code, use a short interval so changes are easy to observe (and change this later when you want your pet to chill out):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;TICK_MS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's one update every ten seconds. Walk away from Kiku and come back later, and its state will definitely be different - they can get cranky when left alone!&lt;/p&gt;

&lt;h2&gt;
  
  
  Give the state a face
&lt;/h2&gt;

&lt;p&gt;Kiku turns its internal state into facial expressions on the Uno Q's LED matrix. We can define its eyes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Eyes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;EYES_OPEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;EYES_CLOSED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;EYES_HAPPY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;EYES_PEEK&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And mouth poses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Mouth&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;MOUTH_FLAT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;MOUTH_SMILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;MOUTH_FROWN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;MOUTH_OPEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;MOUTH_DOT&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now our program can translate state into emotion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;Mouth&lt;/span&gt; &lt;span class="nf"&gt;moodMouth&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="n"&gt;hunger&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;joy&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;3&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;MOUTH_FROWN&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="n"&gt;joy&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&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;MOUTH_SMILE&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;MOUTH_FLAT&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;Kiku isn't actually experiencing happiness or hunger, of course, but a handful of variables and conditionals suddenly starts to look like personality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect actions to behavior
&lt;/h2&gt;

&lt;p&gt;Now the pieces come together. Your main loop has three jobs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Listen for commands.&lt;/li&gt;
&lt;li&gt;Update Kiku's state over time.&lt;/li&gt;
&lt;li&gt;Draw the appropriate expression.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In simplified form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&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="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;typed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&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="n"&gt;typed&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'f'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;feed&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="n"&gt;typed&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'p'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;play&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="n"&gt;typed&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'s'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;toggleSleep&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// update stats when enough time has passed&lt;/span&gt;

  &lt;span class="c1"&gt;// calculate blinking&lt;/span&gt;

  &lt;span class="c1"&gt;// redraw Kiku&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the core architecture of your Kiku. Try it with your own device!&lt;/p&gt;

&lt;p&gt;Open the Serial Monitor in App Lab and type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;f
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kiku eats.&lt;/p&gt;

&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;p
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kiku plays.&lt;/p&gt;

&lt;p&gt;And:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kiku goes to sleep.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;Try to make some changes that alters Kiku's personality. &lt;/p&gt;

&lt;h3&gt;
  
  
  1. Change time
&lt;/h3&gt;

&lt;p&gt;Modify &lt;code&gt;TICK_MS&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Invent an expression
&lt;/h3&gt;

&lt;p&gt;Make Kiku wink, act surprised, or act sad.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Add another command
&lt;/h3&gt;

&lt;p&gt;Maybe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;c → clean
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What did we actually build?
&lt;/h2&gt;

&lt;p&gt;On the surface, we built a cute virtual pet. Underneath, we explored several concepts that appear in much larger systems:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input handling&lt;/strong&gt;: Serial commands become events.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;State management&lt;/strong&gt;: Variables remember what's happening between iterations of the loop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;State transitions&lt;/strong&gt;: Actions modify that state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time-based behavior&lt;/strong&gt;: The system changes even without direct input.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rendering&lt;/strong&gt;: Internal state becomes visible through the LED matrix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Human-computer interaction&lt;/strong&gt;: Animation and timing make simple logic feel expressive.&lt;/p&gt;




&lt;p&gt;If you build your own Kiku for Hacktoberfest, we'd love to see what personality you give it.🌸&lt;/p&gt;




&lt;p&gt;This article is presented as a gift to Hacktoberfest hackers by &lt;strong&gt;Her AI Studio&lt;/strong&gt;, a community program for the next generation of women in AI.&lt;/p&gt;

&lt;p&gt;The complete Kiku workshop, including the full Arduino sketch, exercises, knowledge checks, and additional resources, is available in the &lt;strong&gt;&lt;a href="https://her-ai-studio.github.io/curriculum/mini-workshop.html" rel="noopener noreferrer"&gt;Her AI Studio curriculum&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>arduino</category>
      <category>beginners</category>
      <category>programming</category>
      <category>hacktoberfest</category>
    </item>
    <item>
      <title>Building an Offline Arduino UNO Q Cyberdeck That Identifies Birdsong and Draws Vintage Field Notes</title>
      <dc:creator>Jen Looper</dc:creator>
      <pubDate>Wed, 30 Sep 2026 19:36:21 +0000</pubDate>
      <link>https://dev.to/cloudinary/building-an-offline-arduino-uno-q-cyberdeck-that-identifies-birdsong-and-draws-vintage-field-notes-1cjg</link>
      <guid>https://dev.to/cloudinary/building-an-offline-arduino-uno-q-cyberdeck-that-identifies-birdsong-and-draws-vintage-field-notes-1cjg</guid>
      <description>&lt;p&gt;In this post, I walk through &lt;strong&gt;B.L.O.O.M.&lt;/strong&gt; (Bridging Local Observations, Openly Mapped), an offline cyberdeck built on the &lt;strong&gt;Arduino UNO Q&lt;/strong&gt; that helps naturalists capture field notes, identify birdsong with &lt;strong&gt;Edge Impulse&lt;/strong&gt;, and generate vintage field sketches with &lt;strong&gt;Cloudinary's image generation API&lt;/strong&gt;. It stores everything locally and syncs to a community site only when you choose.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week1-2026-10-05"&gt;Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fezxxqe0immid0smna93b.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fezxxqe0immid0smna93b.jpg" alt="full deck" width="605" height="628"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll learn
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;How to wire an Arduino UNO Q cyberdeck with a USB hub, camera, mic and HDMI screen&lt;/li&gt;
&lt;li&gt;How to run a local Gemma LLM and an audio classifier with Arduino App Lab bricks&lt;/li&gt;
&lt;li&gt;How to retrain an Edge Impulse birdsong model with balanced data&lt;/li&gt;
&lt;li&gt;How to sync SQLite entries to GitHub pull requests from a Netlify Function&lt;/li&gt;
&lt;li&gt;How to generate style-matched images with Cloudinary's image_to_image API with reference images&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cyberdecks are back. The 80s sci-fi dream of a portable, hackable computer rig has been picked back up, especially by nature-loving corners of the internet that want tech that works offline, respects privacy, and looks cool doing it. The one I built for this project lives in a $10 vintage makeup train case and creates &lt;a href="https://autumn-society.org/writing/codex/field-notes/" rel="noopener noreferrer"&gt;Grinnell style field notes&lt;/a&gt; as you tiptoe through the tulips.&lt;/p&gt;

&lt;p&gt;This post covers the &lt;strong&gt;hardware-to-cloud pipeline&lt;/strong&gt;, with the most detail on the part where Cloudinary comes in. The full build (wiring, enclosure, every step) is on &lt;a href="https://www.hackster.io/agent-hawking-1/b-l-o-o-m-a-community-cyberdeck-for-your-field-notes-8dba4c" rel="noopener noreferrer"&gt;Hackster&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The architecture at a glance&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────── In the field (offline) ────────────┐        ┌────────────── Back online ──────────────┐
│                                                │        │                                          │
│  Mic ─► Edge Impulse birdsong model            │        │  Netlify Function                        │
│  Camera ─► photo                               │  POST  │   ├─► Cloudinary image_to_image          │
│  Keyboard/mouse ─► sketch + notes    ─► SQLite ├───────►│   │     (Nano Banana → field sketch)      │
│  Gemma LLM ─► AI note                          │        │   └─► Octokit: open PR on GitHub         │
│                                                │        │          │                               │
│  Arduino UNO Q (App Lab, Chromium kiosk)       │        │   merge ─► Astro rebuild ─► live site    │
└────────────────────────────────────────────────┘        └──────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Nothing leaves the device unless you press Sync to Cloud. That was a hard requirement, as this project is affiliated as well with the nonprofit I founded, &lt;a href="https://heraistudio.org" rel="noopener noreferrer"&gt;Her AI Studio&lt;/a&gt;, where we talk a lot about data governance, security, and privacy, working primarily with local AI on device.&lt;/p&gt;

&lt;p&gt;Here's the full schematic:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F77ysso9nq7xty5r1dyta.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F77ysso9nq7xty5r1dyta.png" alt="schematic" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The core of the cloud side is a single POST to Cloudinary:&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;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Create a field sketch illustration in the style of the reference image [1]..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reference_images"&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;"source_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;"url"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;your reference sketch&amp;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;"model"&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;"nano-banana"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"premium"&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;"target"&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;"target_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;"managed_asset"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"public_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;"field-notes/&amp;lt;slug&amp;gt;-ai"&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;The full function is below.&lt;/p&gt;
&lt;h2&gt;
  
  
  Hardware: the Arduino UNO Q parts list
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Arduino UNO Q (4 GB)&lt;/li&gt;
&lt;li&gt;5" HDMI screen + 6" HDMI cable&lt;/li&gt;
&lt;li&gt;USB 5-in-1 hub&lt;/li&gt;
&lt;li&gt;USB camera&lt;/li&gt;
&lt;li&gt;USB microphone&lt;/li&gt;
&lt;li&gt;Power bank + USB-A to Micro-USB cable&lt;/li&gt;
&lt;li&gt;Bluetooth keyboard with built-in mouse pad&lt;/li&gt;
&lt;li&gt;Vintage train case (or the enclosure you like best! This is the fun part)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;a href="https://docs.arduino.cc/hardware/uno-q/" rel="noopener noreferrer"&gt;Arduino UNO Q&lt;/a&gt; runs everything. The 4 GB variant can run a small language model and an audio classifier fully offline, which is what makes the device useful on a forest trail with no signal...and to explore offline, local AI models on hardware!&lt;/p&gt;

&lt;p&gt;Because it's a cyberdeck, there are peripherals, and those are all connected to the USB hub: screen, mic, camera and power go into the hub, and the hub's USB-C plug goes into the UNO Q. Pair the keyboard and mouse over Bluetooth in Debian and you get those USB ports back.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;At &lt;a href="https://heraistudio.org" rel="noopener noreferrer"&gt;Her AI Studio&lt;/a&gt;, students will get access to this AI Kit.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Running Gemma and birdsong detection on the UNO Q with App Lab
&lt;/h2&gt;

&lt;p&gt;The app is built in Arduino App Lab from three "bricks", which are modular code available pre-built for the Arduino Uno Q:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Web UI for the interface with camera and sketching interface&lt;/li&gt;
&lt;li&gt;LLM brick running Gemma, downloaded locally to the board&lt;/li&gt;
&lt;li&gt;Audio Classification brick for birdsong&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Web UI brick helps you build a tidy interface with a little JS and HTML that runs on your local board via Arduino App Lab. This is how you write your field notes. The LLM brick creates a little space to chat with an LLM and learn more about the flora and fauna photos that you take with the build-in camera.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Side Quest: Fixing Chromium popups on Debian with a kiosk brick&lt;/strong&gt;&lt;br&gt;
On Debian, the stock Web UI brick opened Chromium popups behind other windows. It worked fine on desktop but not on the device. I fixed it with a small custom brick that waits for the local server and then launches Chromium in kiosk mode:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;build_kiosk_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;system&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Linux&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chromium-browser&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--kiosk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--noerrdialogs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--disable-infobars&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--app={url}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nx"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;launch_kiosk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ready_timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;15.0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;wait_for_server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;ready_timeout&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Server never became reachable at {url}&lt;/span&gt;&lt;span class="dl"&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;None&lt;/span&gt;
    &lt;span class="nx"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;build_kiosk_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Launching: {' '.join(cmd)}&lt;/span&gt;&lt;span class="dl"&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;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The interface is presented full-screen without desktop chrome, so the screen is just the field notebook, like this:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxmnv0mfcumfo1sredio3.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxmnv0mfcumfo1sredio3.png" alt=" " width="800" height="783"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Listening for birds
&lt;/h2&gt;

&lt;p&gt;I wanted to give my cyberdeck some "ears", so I turned to a platform that works well to add local, custom-trained models to Arduino via App Lab: Edge Impulse.&lt;/p&gt;

&lt;p&gt;I started from a birdsong classifier in the Edge Impulse community gallery. It had class imbalance and too few samples, so I:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Duplicated the project and removed unusable clips&lt;/li&gt;
&lt;li&gt;Pulled at least 20 recordings each of Blue Jay, Mourning Dove and Northern Cardinal from &lt;a href="https://xeno-canto.org/" rel="noopener noreferrer"&gt;xeno-canto&lt;/a&gt;, a birdsong database&lt;/li&gt;
&lt;li&gt;Converted everything to WAV, labeled it, and uploaded it&lt;/li&gt;
&lt;li&gt;Rebalanced the train/test split and retrained&lt;/li&gt;
&lt;li&gt;Dropped the new model into the App Lab project&lt;/li&gt;
&lt;/ul&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffbprwzm6qzid0t0mqloh.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffbprwzm6qzid0t0mqloh.png" alt="birdsong model" width="799" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When your USB microphone picks up a bird singing, you tap a button, the model classifies the audio, and the result is saved into a field note. The model is &lt;a href="https://studio.edgeimpulse.com/public/1062343/live" rel="noopener noreferrer"&gt;public and cloneable&lt;/a&gt; if you want to add your local species.&lt;/p&gt;
&lt;h2&gt;
  
  
  Syncing: from SQLite to a pull request
&lt;/h2&gt;

&lt;p&gt;Every observation is stored in a local SQLite database, but maybe you want to showcase your field notes to our public repository. When you choose to share, the device posts the entry to a Netlify Function. Photos and sketches are base64-encoded so the whole entry travels as one JSON payload:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sync_entry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;_db_lock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DB_PATH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT id, timestamp, photo, sketch, ai_note, locality, weather, habitat &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FROM entries WHERE id = ?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry_id&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;
        &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fetchone&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;eid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sketch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ai_note&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;locality&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;weather&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;habitat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;eid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;catalog_no&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;catalog_number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eid&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;note&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ai_note&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;locality&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;locality&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;weather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;weather&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;habitat&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;habitat&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;photo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;photo&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&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;photo&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sketch&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sketch&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&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;sketch&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CLOUD_SYNC_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;CLOUD_SYNC_TIMEOUT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="n"&gt;does&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;write&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;It&lt;/span&gt; &lt;span class="n"&gt;opens&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;pull&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="n"&gt;against&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;notes&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;Octokit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="n"&gt;const&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;pr&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;octokit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pulls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;OWNER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;REPO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sb"&gt;`New field note: ${catalog_no}`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;branchName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BASE_BRANCH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Submitted automatically from a Bloom device.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sb"&gt;`**Locality:** ${locality || "—"}`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sb"&gt;`**Weather:** ${weather || "—"}`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sb"&gt;`**Habitat:** ${habitat || "—"}`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aiSketchPath&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;_AI-generated sketch included._&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;device-submission&lt;/span&gt;&lt;span class="sh"&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;This process makes it easier for the community site to get human moderation: a maintainer reviews the PR, merges it, and Netlify rebuilds the Astro site. You can see the results at &lt;a href="https://community-field-notes.netlify.app" rel="noopener noreferrer"&gt;community-field-notes.netlify.app&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9zb7iq4qls6jqr6s7cp0.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9zb7iq4qls6jqr6s7cp0.png" alt="community web" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;Generating AI field sketches with Cloudinary's image_to_image API&lt;br&gt;
Naturalists in the Grinnell tradition paired written notes with drawings. I wanted every synced note to come with an illustration in that vintage style, even if the person who wrote it can't draw.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjgqbgy3zz7146h7a0dbg.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjgqbgy3zz7146h7a0dbg.jpg" alt="AI Field Note" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before the PR is opened, the Netlify Function sends the note text to Cloudinary's image generation API using the image_to_image endpoint and the Nano Banana model. I also pass a reference image, an old-style field sketch already stored in my Cloudinary account, so every output matches the same look:&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;generateAiSketch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;noteText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;slug&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;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Create a field sketch illustration in the style of the reference image [1].&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The sketch should depict:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;noteText&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A natural world observation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Use a vintage field journal aesthetic with earthy tones, hand-drawn quality, and scientific illustration style.&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="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &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;auth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;CLOUDINARY_API_KEY&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;CLOUDINARY_API_SECRET&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="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;base64&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`https://api.cloudinary.com/v2/generate/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;CLOUDINARY_CLOUD_NAME&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/image_to_image`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Basic &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;auth&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="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;reference_images&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;source_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;REFERENCE_IMAGE_URL&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nano-banana&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;tier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;premium&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;target_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;managed_asset&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;public_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`field-notes/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-ai`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// ...handle response, return the asset path for the PR&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  A few things I like about this setup:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The use of the reference image keeps the style consistent, and you can either choose the model, or keep it set to 'auto' - so that the model is chosen for you based on your use case. &lt;/li&gt;
&lt;li&gt;The prompt describes what to draw, and the reference image sets how it should look. Swap the reference and every future note gets a new visual identity.
The target_type: "managed_asset" saves the output straight into my Cloudinary media library under a predictable public_id (field-notes/-ai). I don't have to download and re-upload anything, and the image is ready for delivery to my community field notes app.&lt;/li&gt;
&lt;li&gt;After that it's a normal Cloudinary asset, so the site can deliver it with the usual transformations: f_auto,q_auto, resizing for cards and thumbnails, and so on.&lt;/li&gt;
&lt;li&gt;It's one fetch call. No SDK and no extra infrastructure, which suits a serverless function.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Setup tips
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Keep CLOUDINARY_CLOUD_NAME, CLOUDINARY_API_KEY and CLOUDINARY_API_SECRET in Netlify environment variables. Never put them on the device.&lt;/li&gt;
&lt;li&gt;Upload your reference sketch to Cloudinary first and use its delivery URL as REFERENCE_IMAGE_URL.&lt;/li&gt;
&lt;li&gt;The Image Generation API has a free monthly allowance, which covers a community project like this. If you don't have an account yet, sign up for free.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The enclosure: no 3D printer needed
&lt;/h2&gt;

&lt;p&gt;Instead of printing a case, I reused one (preferable for cyberdecks!): a vintage Lancôme makeup train case, costing about $10 on eBay. Here's what I did to fit everything in the case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cut a small hole in the side for the camera lens&lt;/li&gt;
&lt;li&gt;Build shelves from recycled foam and fabric&lt;/li&gt;
&lt;li&gt;Zip-tie the USB hub in place&lt;/li&gt;
&lt;li&gt;Made a cardboard backing for the screen, with room for cables&lt;/li&gt;
&lt;li&gt;Gave the UNO Q its own spot, with airflow in mind (foam and fabric trap heat, so watch temperatures or use an open weave like rattan - I've even used a sushi rolling mat for this type of shelf)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tall case also leaves room for a magnifier, pressed flowers, a small watercolor set, or a leaf guide.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9nl6zhy3wsoq1wcpl7me.jpeg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9nl6zhy3wsoq1wcpl7me.jpeg" alt="in the field" width="412" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;the author enjoying a Fall day of field notes&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Go touch grass: using B.L.O.O.M. in the field
&lt;/h2&gt;

&lt;p&gt;Power it on, head outside, and record what you find: birdsong, photos, sketches, questions for the LLM, locality, weather and habitat. When you're back on Wi-Fi, press sync to send the note to GitHub, get a Cloudinary-generated sketch, and add it to a shared natural-history record.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faweupf1wnvo0ko75w7sx.jpeg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faweupf1wnvo0ko75w7sx.jpeg" alt="cat" width="416" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Discover new fauna&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;📖 Full build on Hackster: &lt;a href="https://www.hackster.io/agent-hawking-1/b-l-o-o-m-a-community-cyberdeck-for-your-field-notes-8dba4c" rel="noopener noreferrer"&gt;B.L.O.O.M.: A Community Cyberdeck for your Field Notes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💻 Device code:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Her-AI-Studio" rel="noopener noreferrer"&gt;
        Her-AI-Studio
      &lt;/a&gt; / &lt;a href="https://github.com/Her-AI-Studio/BLOOM-code" rel="noopener noreferrer"&gt;
        BLOOM-code
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      This is the codebase for use in Arduino App Lab, for the Arduino Uno Q in a cyberdeck
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;B.L.O.O.M — an offline field journal&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;Bridging Local Observations, Openly Mapped&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;B.L.O.O.M&lt;/strong&gt; turns an Arduino® UNO Q into a self-powered, offline field-journal cyberdeck: point it at a plant, an animal, or anything worth noticing on a hike, capture a photo, sketch on top of it, and talk through what you're observing with a fully local AI model. Every entry — photo, sketch, and note — is saved to the board itself and stays there unless you deliberately choose to sync it to a companion website. No connectivity is required to use it in the field.&lt;/p&gt;

&lt;p&gt;Built for Her AI Studio's curriculum and submitted for hackathons including the &lt;a href="https://www.hackster.io/contests/invent-the-future-with-arduino-uno-q-and-app-lab" rel="nofollow noopener noreferrer"&gt;Invent the Future with Arduino UNO Q and App Lab&lt;/a&gt; contest, Best Social Impact category — the pitch is a countermeasure to phone-and-camera culture: a single-purpose device that makes you stop, point, and wait for a note instead of snapping and swiping past.&lt;/p&gt;

&lt;p&gt;…&lt;/p&gt;&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Her-AI-Studio/BLOOM-code" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;🌐 &lt;a href="https://community-field-notes.netlify.app" rel="noopener noreferrer"&gt;Community web app (Astro + Netlify)&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Her-AI-Studio" rel="noopener noreferrer"&gt;
        Her-AI-Studio
      &lt;/a&gt; / &lt;a href="https://github.com/Her-AI-Studio/field-notes" rel="noopener noreferrer"&gt;
        field-notes
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      This is the core code of the offline, locally-powered cyberdeck for use when hiking
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Field Notes&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A digital field journal — a collection of natural history observations, surveys, and records from the field.&lt;/p&gt;
&lt;p&gt;Built with &lt;a href="https://astro.build" rel="nofollow noopener noreferrer"&gt;Astro&lt;/a&gt; and Markdown content collections.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Project Structure&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;/
├── public/
│   └── favicon.svg
├── src/
│   ├── content/
│   │   ├── config.ts
│   │   └── notes/
│   │       ├── observation-of-the-red-shouldered-hawk.md
│   │       ├── botanical-survey-of-coastal-dune-ecology.md
│   │       ├── geological-observations-along-the-appalachian-trail.md
│   │       └── nocturnal-insect-survey-june.md
│   ├── layouts/
│   │   └── Layout.astro
│   ├── pages/
│   │   ├── index.astro
│   │   └── notes/
│   │       └── [...slug].astro
│   └── styles/
│       └── global.css
└── package.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Adding a New Note&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Create a new &lt;code&gt;.md&lt;/code&gt; file in &lt;code&gt;src/content/notes/&lt;/code&gt; with frontmatter:&lt;/p&gt;
&lt;div class="highlight highlight-source-yaml notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;---
&lt;span class="pl-ent"&gt;title&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;Your Note Title&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-ent"&gt;date&lt;/span&gt;: &lt;span class="pl-c1"&gt;2026-07-21&lt;/span&gt;
&lt;span class="pl-ent"&gt;location&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;Location, State&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-ent"&gt;excerpt&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;A brief summary of the observation.&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-ent"&gt;image&lt;/span&gt;: &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;/images/your-image.jpg&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-ent"&gt;tags&lt;/span&gt;: &lt;span class="pl-s"&gt;["tag1", "tag2"]&lt;/span&gt;
---&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The note will automatically appear on the…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Her-AI-Studio/field-notes" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;🐦 &lt;a href="https://studio.edgeimpulse.com/public/1062343/live" rel="noopener noreferrer"&gt;Edge Impulse birdsong model&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;p&gt;Overall, Arduino&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;Did you know the Arduino Uno Q runs Linux when it's a single-board computer? Without open source, we'd be locked into a proprietary sandbox. Instead, we can build in the open, and enhance the tools and models that others have generously open-sourced.&lt;/p&gt;




&lt;p&gt;If you build a nature-loving cyberdeck like this, contribute a note to the website, or retrain the model for birds near you, share it in the comments. I'd like to see what other people's field notes look like and what you're looking at in your region. 🌿&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Cloudinary ❤️ developers&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ready to level up your media workflow? Start using Cloudinary for free and build better visual experiences today.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👉 &lt;strong&gt;&lt;a href="https://link.cloudinary.com/utcCA" rel="noopener noreferrer"&gt;Create your free account&lt;/a&gt;&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>arduino</category>
      <category>iot</category>
      <category>ai</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>Is AI Cheugy?</title>
      <dc:creator>Jen Looper</dc:creator>
      <pubDate>Mon, 21 Sep 2026 04:00:00 +0000</pubDate>
      <link>https://dev.to/heraistudio/is-ai-cheugy-1j01</link>
      <guid>https://dev.to/heraistudio/is-ai-cheugy-1j01</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;cheu·​gy&lt;/strong&gt; ˈchü-gē - cheugier; cheugiest. slang: uncool, unfashionable&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's happened enough times that I'm starting to call it a trend: I talk about something - anything - AI-related, and my listener screws their face into a scowl, gives me a robust side eye glare, and beats a hasty retreat. It's as if, when I mention AI, people suddenly are accosted by a bad smell...the stench of the dangerous and icky.&lt;/p&gt;

&lt;p&gt;The AI-pilled amongst us, of course, don't really see this. So caught up in their bubble, so sure of the rightness of their course, so excited by the newness of the tech and so enthused by its promise, they are often blind to the legitimate concerns that many (many!) so-called "normies" have about AI. Anyway, they don't talk to normies.&lt;/p&gt;

&lt;h3&gt;
  
  
  So let's talk about it.
&lt;/h3&gt;

&lt;p&gt;At Her AI Studio, for which this is an interesting inaugural blogpost, our mission is to empower high school aged girls to learn about AI in a safe space - a space for women, coached by female mentors near their age, using physical AI to build cyberdecks. By using locally-run hardware on which we can run small local AI models, we create a "walled garden" where students can safely experiment with local AI. In doing so, we can also discuss weighty topics of privacy, security, data governance, and the ethical questions surrounding AI.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpbxjggkm4uqi4ryh1gsr.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpbxjggkm4uqi4ryh1gsr.png" alt="Her AI Studio AI Kit" width="800" height="1067"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A cyberdeck built with our kit, including an Arduino Uno Q board, small keyboard and monitor, microphone, camera, and USB Hub. Every student can create their own, this is just my fluffy iteration.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That doesn't make AI any less cheugy (and, increasingly, political, although that's a topic for another day). There are serious question about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;From where the training data that has fed the models is sourced&lt;/li&gt;
&lt;li&gt;What this technology means for the environment, from training, sourcing materials for hardware, water use, pollution, land use, and more &lt;/li&gt;
&lt;li&gt;How AI is &lt;em&gt;really&lt;/em&gt; screwing up education (my own particular hot take) and is being actively banned in some districts&lt;/li&gt;
&lt;li&gt;How deepfakes are ruining lives&lt;/li&gt;
&lt;li&gt;How powerful algorithms are feeding propaganda machines and influencing political outcomes&lt;/li&gt;
&lt;li&gt;How copyright rules and ethical concerns about plagiarism are caught up in this new technology&lt;/li&gt;
&lt;li&gt;How skills are decaying due to being replaced by a technology that can outsource work in a flash&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;...and more&lt;/p&gt;

&lt;h3&gt;
  
  
  Headwinds
&lt;/h3&gt;

&lt;p&gt;It's really important to acknowledge these important headwinds and concerns and understand them. Rather than brush anything aside, I propose to address these head on in our cohorts with full transparency and with the understanding that there are many unsavory aspects of this tech. Reactions such as this from Gen Z commenters crystalize the moment. When seeking applicants for our Youth Board, here's a reaction:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fykc7gp3b3d585oe8tgom.jpeg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fykc7gp3b3d585oe8tgom.jpeg" alt="slop board" width="709" height="1536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ouch. But point well taken, and gone are the AI images from the site. We are listening.&lt;/p&gt;

&lt;p&gt;It's a fine line we're threading, and the gulf is enormous. At one end, we have folks insisting that AI will be so powerful that in a few years it will end us all. At the other end we have folks who advocate banning it entirely. &lt;/p&gt;

&lt;p&gt;There has to be some kind of middle ground. The companies making foundational models aren't doing us any favors by promoting the idea that somehow they have created something that will imminently cause a world cataclysm. By not sufficiently creating robust guardrails to protect users from abuse, they are creating a hostile environment for large swaths of the population. &lt;/p&gt;

&lt;p&gt;This is precisely why we founded Her AI Studio. We want to ensure that women will have a role to play in the &lt;em&gt;creation&lt;/em&gt; of AI, in the design of its guardrails, in the ethics around the kind of data on which it is trained. Without radical inclusion of these voices, we risk creating an "intelligence" that is stunted, that does not resemble our reality, and that cannot meet the needs of 50% of the world's population.&lt;/p&gt;

&lt;h3&gt;
  
  
  A new approach
&lt;/h3&gt;

&lt;p&gt;We do not approach the topic from a place of weakness or fear. We are not evangelizing AI, or really any particular product. Instead, we use open models, run locally, on hardware that we own, and talk about what this means. We study how data is used, from where it's sourced, what ethical concerns are raised by both building and using AI, and more. &lt;/p&gt;

&lt;p&gt;Because if we stay on our current path, we are going to build an AI that is not only unrecognizable, it's not going to be built to serve the best interests of a lot of people. And we find that unacceptable. Unacceptable and truly cheugy.&lt;/p&gt;

&lt;p&gt;Visit &lt;a href="https://heraistudio.org" rel="noopener noreferrer"&gt;HerAIStudio.org&lt;/a&gt; to learn more.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl0n8oklzeejcy9d6ykn9.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl0n8oklzeejcy9d6ykn9.png" alt="Her AI Studio logo" width="800" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>career</category>
    </item>
    <item>
      <title>Kitchen-Sune: A Community Cookbook</title>
      <dc:creator>Jen Looper</dc:creator>
      <pubDate>Sun, 16 Aug 2026 03:35:03 +0000</pubDate>
      <link>https://dev.to/jenlooper/kitchen-sune-a-community-cookbook-5fea</link>
      <guid>https://dev.to/jenlooper/kitchen-sune-a-community-cookbook-5fea</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/frontend-2026-07-29"&gt;Frontend Challenge - Comfort Food Edition, Perfect Landing&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;This is a blast from the past, but it's so lovely I couldn't resist submitting it for this challenge.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://kitchen-sune.netlify.app/" rel="noopener noreferrer"&gt;Kitchen-Sune&lt;/a&gt; is a community-driven international recipe book built in collaboration with Front-End Foxes members when we pivoted our nonprofit's efforts from in-person workshops to an international online boot camp during the pandemic. It brings together comfort food recipes from around the globe into a sleek, accessible, and user-friendly Vuepress (later refactored to Vitepress) web application where food lovers and developers alike can discover new dishes.&lt;/p&gt;

&lt;p&gt;We were happy to host recipes from Ukraine, Kenya, Nigeria, and everywhere in between. Between Jalebi Babies, Moin-Moin, Puff Puff, and Strawberry Mush, we've got you covered for comfort food!&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Live Demo: &lt;a href="https://kitchen-sune.netlify.app/" rel="noopener noreferrer"&gt;Kitchen-Sune App&lt;/a&gt;&lt;br&gt;
GitHub Repo: &lt;a href="https://github.com/FrontEndFoxes/kitchen-sune" rel="noopener noreferrer"&gt;https://github.com/FrontEndFoxes/kitchen-sune&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check out a preview of the recipe book (this recipe for maple syrup candy came with a video):&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdhbk0ha2788ov0jgddlu.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdhbk0ha2788ov0jgddlu.png" alt="Candy video" width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Journey
&lt;/h2&gt;

&lt;p&gt;Revisiting and showcasing this project was a nostalgic process. Working on a community recipe project taught me the importance of building inclusive, easy-to-navigate web interfaces for diverse content as an educatioal tool. We used to use this repo as a way to train boot camp enrollees in how to use GitHub and make a PR to a repo. &lt;/p&gt;

&lt;p&gt;What I Learned: No matter where we are in our journey, food brings us together.&lt;/p&gt;

&lt;p&gt;What I'm Proud Of: How timeless and clean the aesthetic remains, making it easy for anyone to find a warm, cozy meal to cook or a snack to throw together (Pandemic Cookies, anyone?).&lt;/p&gt;

&lt;p&gt;What's Next: Let's keep going! Add your recipe via a PR to the GitHub repo. And I'd love to have more photos to show off your work. &lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>frontend</category>
      <category>showdev</category>
      <category>vue</category>
    </item>
    <item>
      <title>Resize One Image into 6 Social Media Formats Automatically Using Cloudinary Claimable Clouds</title>
      <dc:creator>Jen Looper</dc:creator>
      <pubDate>Wed, 05 Aug 2026 21:30:34 +0000</pubDate>
      <link>https://dev.to/cloudinary/resize-one-image-into-6-social-media-formats-automatically-using-cloudinary-claimable-clouds-3gfc</link>
      <guid>https://dev.to/cloudinary/resize-one-image-into-6-social-media-formats-automatically-using-cloudinary-claimable-clouds-3gfc</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Claimable Clouds are temporary Cloudinary environments for AI workflows that let AI Agents safely manage media with no signup required.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Imagine you're a busy designer, with many satisfied clients who depend on you to take their images and make them look great across social media. All that manual cropping and scaling, it's enough to make a body cry.&lt;/p&gt;

&lt;p&gt;On top of that, you know that AI can give you a hand here, but managing the handoff between your AI, your own skilled hands and artistic taste and style, and your always-in-a-hurry client list is another big pain.&lt;/p&gt;

&lt;p&gt;Enter the concept of the Cloudinary Claimable Cloud, just released today.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://cloudinary.com/documentation/claimable_cloud_provisioning" rel="noopener noreferrer"&gt;Take a look at the docs&lt;/a&gt; about these new temporary instances available now&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What we built and why
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Provision a disposable Cloudinary cloud with no signup, using &lt;code&gt;npx @cloudinary/cloud&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Auto-detect a dropped image and upload it to that temporary cloud&lt;/li&gt;
&lt;li&gt;Auto-crop it into 6+ social formats (Instagram, LinkedIn, X, Facebook, Stories) using AI-based smart cropping&lt;/li&gt;
&lt;li&gt;Generate a side-by-side gallery of results automatically&lt;/li&gt;
&lt;li&gt;Hand off a Claim URL so a client can make the cloud permanent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, you can hand off the main pain points to AI - the resizing and reshaping of your images for the various social media platforms, while giving your clients a clean handoff via a temporary Cloud environment that they can use to create a Cloudinary account and start using these assets.&lt;/p&gt;

&lt;p&gt;One side effect: this also nudges your whole client base toward the same toolset - Cloudinary. The bigger deal is working with an AI agent that makes your life easier but ALSO allows you to keep control of the output. &lt;/p&gt;

&lt;p&gt;Let's walk through how this works! It all boils down to a new command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @cloudinary/cloud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Type that into your terminal to kick off the process. &lt;/p&gt;

&lt;p&gt;I built a &lt;a href="http://github.com/jlooper/social-me" rel="noopener noreferrer"&gt;small app&lt;/a&gt; around this concept to provide this AI agent with a simple harness, so let me show how that looks. The user experience is to drop any image you want resized into the &lt;code&gt;/drop&lt;/code&gt; folder. Under the cover, there are a few more processes at work: &lt;/p&gt;

&lt;h2&gt;
  
  
  Provisioning: Spin Up a Temporary Cloudinary Cloud
&lt;/h2&gt;

&lt;p&gt;On the first run, the app spins up a disposable Cloudinary "Claimable Cloud" via &lt;code&gt;npx @cloudinary/cloud&lt;/code&gt; with temporary credentials, so no signup is required. The generated credentials are saved temporarily in a .env file locally. This gives the tool a live upload/delivery endpoint without any manual account setup. These credentials are refreshed when you start the app: &lt;code&gt;npm i &amp;amp;&amp;amp; npm start&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watch &amp;amp; Transform: Auto-Crop for Each Social Platform
&lt;/h2&gt;

&lt;p&gt;The app watches the &lt;code&gt;drop&lt;/code&gt; folder for new images. When a high-resolution master image lands there, it's uploaded to this temporary Cloudinary instance. &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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffnb9cfto74uahxogcb89.jpeg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffnb9cfto74uahxogcb89.jpeg" alt="a Nano Banana image" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The image we want to resize&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then it's run through a set of predefined social-media format specs (Instagram Square, Portrait, Story/Reels/TikTok, LinkedIn, X/Twitter, Facebook) using dynamic transformations like c_fill,g_auto for smart auto-cropping to each platform's exact aspect ratio and dimensions, focusing on the most important element. Control these formats and transformations in the &lt;code&gt;src/formats.js&lt;/code&gt; file:&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FORMATS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;instagram-square&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Instagram Square Post&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;ratio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1:1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1080&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1080&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;buildTransformation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;subjectFill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1080&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1080&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;instagram-portrait&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Instagram Feed Portrait&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;ratio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;4:5&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1080&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1350&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;buildTransformation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;subjectFill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1080&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1350&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;instagram-story&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Instagram Story / Reels / TikTok&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;ratio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;9:16&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1080&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1920&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;buildTransformation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;padWithBlurredBackground&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1080&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1920&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&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;h2&gt;
  
  
  Render Results &amp;amp; Send to Client: Share a Claim URL
&lt;/h2&gt;

&lt;p&gt;The temporary Claimable Cloud is provisioned using the temporary credentials stored in .env:&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="c1"&gt;// Ensures a usable CLOUDINARY_URL is in process.env, provisioning a disposable&lt;/span&gt;
&lt;span class="c1"&gt;// cloud on demand. Returns { claimUrl } (claimUrl is undefined when a cloud&lt;/span&gt;
&lt;span class="c1"&gt;// was already configured before this run).&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ensureCloud&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;loadEnv&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;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CLOUDINARY_URL&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;claimUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CLOUDINARY_CLOUD_CLAIM_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;provisioned&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No Cloudinary cloud configured yet — provisioning a disposable one (no signup needed)...&lt;/span&gt;&lt;span class="dl"&gt;'&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;raw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;execFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;npx&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-y&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@cloudinary/cloud&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;create&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--no-agent-metadata&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="na"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&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="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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;combined&lt;/span&gt; &lt;span class="o"&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;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdout&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stderr&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&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="nf"&gt;trim&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="nf"&gt;redact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;combined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&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;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseKeyValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;loadEnv&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;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CLOUDINARY_URL&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;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cloud provisioning did not produce a CLOUDINARY_URL.&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;claimUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CLAIM_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;provisioned&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;Generated variant URLs are written to manifest.json and rendered into an auto-generated side-by-side HTML gallery, which opens automatically. The output also includes a Claim URL, letting the disposable cloud be handed off (e.g., to a client) and made permanent at any time.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvntpjxpac7lcfhshn7z4.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvntpjxpac7lcfhshn7z4.png" alt="The results" width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The temporary link and generated gallery of images to pick from. Note the fancy Instagram one!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now you can pass that Claim URL right on to your client and start the process of allowing them to claim the results and start working with them. &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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmqmu2njweqs5sqfsd2xv.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmqmu2njweqs5sqfsd2xv.png" alt="login box for claimable cloud" width="800" height="677"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is an early feature, with more agent-based tools coming from Cloudinary soon. Watch for more interesting work around AI agents in the near future!&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Cloudinary ❤️ developers&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ready to level up your media workflow? Start using Cloudinary for free and build better visual experiences today.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👉 &lt;strong&gt;&lt;a href="https://link.cloudinary.com/ur1AT" rel="noopener noreferrer"&gt;Create your free account&lt;/a&gt;&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Cloudinary's New Image Generation API: One API, Multiple AI Models, and Built-in Asset Management</title>
      <dc:creator>Jen Looper</dc:creator>
      <pubDate>Wed, 08 Jul 2026 13:02:03 +0000</pubDate>
      <link>https://dev.to/cloudinary/cloudinarys-new-image-generation-api-one-api-multiple-ai-models-and-built-in-asset-management-3ldl</link>
      <guid>https://dev.to/cloudinary/cloudinarys-new-image-generation-api-one-api-multiple-ai-models-and-built-in-asset-management-3ldl</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;At conference booths, developers often ask whether we support image generation at Cloudinary, given our emphasis on media management. As of now, I can say "YES! yes, we do" - here's how!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cloudinary's Image Generation API lets developers generate images from text prompts using multiple AI model families, then store the result as a managed Cloudinary asset for delivery, optimization, resizing, and transformation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Original image copyright © REUTERS/ABC Affiliate WABC&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this tutorial, we'll use Python to call the API, generate an image, and save the final result to Cloudinary.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll build
&lt;/h2&gt;

&lt;p&gt;A Python script that can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate an image from a text prompt&lt;/li&gt;
&lt;li&gt;Choose a model family like &lt;code&gt;flux&lt;/code&gt;, &lt;code&gt;recraft&lt;/code&gt;, &lt;code&gt;gpt-image&lt;/code&gt;, &lt;code&gt;ideogram&lt;/code&gt;, or &lt;code&gt;nano-banana&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Save generated output as a managed Cloudinary asset&lt;/li&gt;
&lt;li&gt;Print the final image URL, public ID, size, and file size&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;First, follow &lt;a href="https://cloudinary.com/documentation/image_generation_addon" rel="noopener noreferrer"&gt;the instructions on Cloudinary docs&lt;/a&gt; to install the image generation add-on to your account. While you're in the Cloudinary console, make note of your API key, secret and the cloud name where you want assets to be stored.&lt;/p&gt;

&lt;p&gt;Install the Python dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Export your Cloudinary credentials:&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;export &lt;/span&gt;&lt;span class="nv"&gt;CLOUDINARY_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your-api-key"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CLOUDINARY_API_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your-api-secret"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CLOUDINARY_CLOUD_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your-cloud-name"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's your &lt;a href="https://gist.github.com/jlooper/4ef0ac33a6bd6e943d953f523d606412" rel="noopener noreferrer"&gt;script&lt;/a&gt;. Save it as 'generate.py'.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate your first AI image
&lt;/h2&gt;

&lt;p&gt;Run the script with a prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 generate.py &lt;span class="s2"&gt;"A medieval monk"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Farplam1tplas8pfkhdaf.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Farplam1tplas8pfkhdaf.png" alt=" " width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This image was generated with the default Flux model&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The script sends a request to Cloudinary's Image Generation API:&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;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;family&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;model_family&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tier&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tier&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;target&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;target_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;managed_asset&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;public_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;public_id&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;Then it calls the API using HTTP Basic Auth:&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;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;IMAGE_GEN_BASE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/generate/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cloud_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/text_to_image&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CLOUDINARY_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CLOUDINARY_API_SECRET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;90&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;h2&gt;
  
  
  Choose an AI image model
&lt;/h2&gt;

&lt;p&gt;This is the fun part. You can pick the image generation model that best suits your use case. For example, I find that nano-banana works well with images that include text.&lt;/p&gt;

&lt;p&gt;You can switch model families without changing the rest of your application code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 generate.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"A futuristic Tokyo skyline at sunset"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--model&lt;/span&gt; flux &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tier&lt;/span&gt; premium
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Supported model families include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flux
gpt-image
ideogram
recraft
nano-banana
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe9vz84u8iq56c3vs7lfv.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe9vz84u8iq56c3vs7lfv.png" alt="Another monk" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This was the same prompt, done with Ideogram model&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That makes it easier to test different visual styles while keeping one integration path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Save generated images as Cloudinary assets
&lt;/h2&gt;

&lt;p&gt;The important part of the request is this:&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;target&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;target_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;managed_asset&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;public_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;public_id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells Cloudinary to save the generated image as a managed asset on your account, instead of returning only a temporary output.&lt;/p&gt;

&lt;p&gt;After generation, the script prints something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Image ready!
Public ID: generated/fox-hiking
URL: https://res.cloudinary.com/...
Size: 1024×1024 px
File size: 840 KB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the image is in Cloudinary, you can resize it, optimize it, crop it, transform it, and deliver it through Cloudinary's CDN. Wicked fast and easy!&lt;/p&gt;

&lt;h2&gt;
  
  
  Full usage examples
&lt;/h2&gt;

&lt;p&gt;Generate a new image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 generate.py &lt;span class="s2"&gt;"Marie de France playing pool"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuv5iwudlfmr3567cibqm.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuv5iwudlfmr3567cibqm.png" alt="Marie de France" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I love this image! Marie de France was a 12th century author of some famous french literature that I particularly like. I don't think she ever played pool, though. Also this pool table is awesomely 'AI-pilled"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Use a premium model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 generate.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"A cinematic product photo of a sneaker floating over water"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--model&lt;/span&gt; flux &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tier&lt;/span&gt; premium
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why this is useful
&lt;/h2&gt;

&lt;p&gt;Most image generation APIs return an output.&lt;/p&gt;

&lt;p&gt;Cloudinary's Image Generation API returns an output that can immediately become part of your media pipeline.&lt;/p&gt;

&lt;p&gt;That means developers can generate an image and then use the same platform to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store it&lt;/li&gt;
&lt;li&gt;Transform it&lt;/li&gt;
&lt;li&gt;Optimize it&lt;/li&gt;
&lt;li&gt;Resize it&lt;/li&gt;
&lt;li&gt;Deliver it&lt;/li&gt;
&lt;li&gt;Reuse it across applications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For apps that already manage media with Cloudinary, AI image generation becomes part of the existing workflow instead of a separate one-off process. The pipeline just got way simplified for you!&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;This Python script is small, but it covers the core production workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Authenticate with Cloudinary&lt;/li&gt;
&lt;li&gt;Send a prompt to the Image Generation API&lt;/li&gt;
&lt;li&gt;Choose a model family and tier&lt;/li&gt;
&lt;li&gt;Save the result as a managed asset&lt;/li&gt;
&lt;li&gt;Return a usable image URL&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you're building developer tools, e-commerce workflows, campaign generators, or AI-powered creative apps, this approach gives you both image generation and image delivery in the same pipeline.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Cloudinary ❤️ developers&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ready to level up your media workflow? Start using Cloudinary for free and build better visual experiences today.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👉 &lt;strong&gt;&lt;a href="https://link.cloudinary.com/urmty" rel="noopener noreferrer"&gt;Create your free account&lt;/a&gt;&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>ai</category>
      <category>nanobanana</category>
      <category>codenewbie</category>
      <category>images</category>
    </item>
    <item>
      <title>Magnificent Humanity, Building Cities, and a Special Announcement!</title>
      <dc:creator>Jen Looper</dc:creator>
      <pubDate>Fri, 05 Jun 2026 19:37:39 +0000</pubDate>
      <link>https://dev.to/jenlooper/magnificent-humanity-building-cities-and-a-special-announcement-54pf</link>
      <guid>https://dev.to/jenlooper/magnificent-humanity-building-cities-and-a-special-announcement-54pf</guid>
      <description>&lt;p&gt;I'm back from a great vacation and side-quest experience in Asia, where one can always find brilliant human interactions even with imperfect linguistic competence. For example, having a haircut at a random salon in Taipei where the little nephew of the stylist offered fashion advice on the spot, gravely shaking his head at short haircuts for ladies, and anyway why didn't I dye it purple? Or a &lt;a href="https://x.com/jenlooper/status/2061302787963535710?s=20" rel="noopener noreferrer"&gt;photoshoot&lt;/a&gt; on a bamboo raft on the Li river in the Zhuang autonomous region, with full-on Chinese style makeup in local costume for a solid two hours in the boiling sun with a patient photog.&lt;/p&gt;

&lt;p&gt;While I was on vacation, Pope Leo published something important. It turns out, the "Woke Pope" is participating in an ever-more-woke religious discourse straight outta the Vatican, and as a borderline agnostic Protestant, I found it incredibly empowering. His "Magnifica Humanitas" encyclical is the thought leadership core document that many of us have looked for - a work that doesn't land on either the AI-hater or the All-In-AI extreme, but, in the best devout tradition, looks to religious texts for guidance to discover narratives that can guide our steps as we try to build with AI while not wrecking the world. Let's talk about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Encyclical
&lt;/h2&gt;

&lt;p&gt;This &lt;a href="https://www.vatican.va/content/leo-xiv/en/encyclicals/documents/20260515-magnifica-humanitas.html" rel="noopener noreferrer"&gt;document&lt;/a&gt; is long and wide-ranging. It's broken into five chapters; the first two discuss the Catholic Church's evolving concept of Social Doctrine, the third talks about how AI is being built and dives into the narratives surrounding this building effort, the fourth discusses concepts of human dignity and freedom within this evolving ecosystem, and the final chapter offers a critique of the current ever-more ferocious culture of war and the aggressive exercise of raw power, citing the need to "disarm" these systems to build a "civilization of love". It's a beautiful vision, and a multimedia experience; there are videos and infographics - this is a Pope who well-understands the internet and the power of imagery.&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%2Fznmrds507j1pg16el7h5.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%2Fznmrds507j1pg16el7h5.png" alt="Risks vs Humane AI" width="800" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Risks vs Humane AI&lt;/p&gt;

&lt;p&gt;I didn't have it in my 2026 Bingo card to have the Pope exhorting software engineers: "Developers ... bear a particular ethical and spiritual responsibility, for every design choice reflects a vision of humanity. Just as the creator of an artistic or literary work must consider the values it conveys, so developers are called to embed values in their projects with due seriousness: with transparency, responsibility toward affected communities and careful attention to ensuring that what is being cultivated is a genuine good."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Parables and Exemplars
&lt;/h2&gt;

&lt;p&gt;One motif that stood out to me is the Pope's insistence that, as part of the magnificence of humanity, every person is of value. But how can that value be measured? Rather than valuing the human by their output, society is better served by valuing the dignity of work itself. Stemming from the crisis of work triggered by the industrial revolution, the Church's social doctrine is evolving to cover a similar crisis around ensuring that everyone is afforded the ability to contribute to the social good via their work. "The human person is an end, not a means" flies directly in the face of much of the &lt;a href="https://www.adventuresinoss.com/aws-four-years/" rel="noopener noreferrer"&gt;toxic corporate culture&lt;/a&gt; prevailing today.&lt;/p&gt;

&lt;p&gt;The focus on recentering human beings within this high-tech moment has led some folks to closely examine the narratives presented in the document. One thing that longstanding religious traditions do well is use stories and parables to teach underlying values. The Pope leans on the well-known story of the Tower of Babel and the less well-known story of Nehemiah's rebuilding of Jerusalem to show 'exemplars'. Rather than creating a grandiose, yet dehumanizing tower where communication breaks down, we are encouraged to look at the way Nehemiah organized the collaborative rebuilding of a city for the greater good. This is a model of building software - AI or other - that benefits the many, rather than just a few oligarchs.&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%2Fqbr8433ftzwerwq0asdo.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%2Fqbr8433ftzwerwq0asdo.png" alt="Two Cities, Two Models" width="800" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Two Cities, Two Models&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One interesting aspect of this encyclical is the use of inclusive language and the special attention paid to those left behind by technological advances. The Pope takes care to address "men and women of goodwill" - an improvement on similar writing of the past, and notes that "there is still a long way to go to ensure that the rights of a great many, namely women, are equally and genuinely guaranteed throughout the world." But there is little acknowledgement about the specific ways that women have backslid in recent years or what specifically we should do to ensure equity.&lt;/p&gt;

&lt;p&gt;I'll be honest here. I'm really worried about how people who identify as women are showing up in this new world. Starting in the mid 2010s, we worked at the grass roots to build community, launch initiatives, press companies for diversity programming, and create inclusive education. Today I'm seeing women-focused grass-roots orgs shut down or sucked into a corporation completely divorced from their grass-roots origin (WWC, I'm looking at you). In today's climate of oligarchy, technocracy, bare-knuckled corporate competition, and the dominance of silicon valley in AI discourse, I'm hard pressed to name 20+ visible female leaders who are influencing how AI is being built from an engineering standpoint. We all saw &lt;a href="https://www.ndtv.com/world-news/donald-trump-xi-jinping-summit-sparks-backlash-over-one-striking-detail-no-women-on-table-11502112" rel="noopener noreferrer"&gt;that awful photo&lt;/a&gt; of the Trump/Xi summit, with ZERO women at the table. This image says 1000 words. It's time to muscle back in to make sure we have a seat at the table so that we can build the kind of AI reality the Pope is envisioning.&lt;/p&gt;

&lt;p&gt;We are in fact entering a perfect storm of underrepresentation, gaps in perception, and disproportional impact of AI on women's careers. According to &lt;a href="https://www.forbes.com/sites/joemckendrick/2025/04/22/women-make-gains-in-ai-but-roadblocks-remain" rel="noopener noreferrer"&gt;Forbes&lt;/a&gt;, &lt;a href="https://www.axios.com/2026/04/01/leanin-sheryl-sandberg-ai-women-men-work" rel="noopener noreferrer"&gt;Axios&lt;/a&gt;, and &lt;a href="https://www.weforum.org/stories/2025/06/amnc25-what-to-know-about-ai-and-the-gender-gap" rel="noopener noreferrer"&gt;panels at Davos&lt;/a&gt;, growth is slow, gaps remain large, and women are especially hard-hit by AI disruption.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Women in AI engineering and research: slowly increasing but still underrepresented&lt;/li&gt;
&lt;li&gt;Women in AI leadership: Increasing, but leadership gap remains large&lt;/li&gt;
&lt;li&gt;Women using GenAI at work: Lower adoption than men&lt;/li&gt;
&lt;li&gt;Women receiving recognition for AI use: Lower than men, even when usage is similar&lt;/li&gt;
&lt;li&gt;Jobs most exposed to AI disruption: Disproportionately female-dominated occupations&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  So what are we going to do to close this gender gap?
&lt;/h3&gt;

&lt;h2&gt;
  
  
  A New City
&lt;/h2&gt;

&lt;p&gt;Pope Leo leans on a comparison between two polar opposite models of building. But I propose that there's a third model that was described in the late Middle Ages by one of the first feminist writers in the West: Christine de Pizan, who wrote a rebuttal to the current fashionable narrative of her times, the Roman de la Rose. Rejecting this misogynistic (albeit wildly popular) book, she wrote her own vision of a new city: &lt;strong&gt;The City of Ladies&lt;/strong&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%2Fj4nn4cj9a0zlwg54p0pq.jpg" 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%2Fj4nn4cj9a0zlwg54p0pq.jpg" alt="Christine listens, learns, and builds" width="800" height="526"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Christine listens, learns, and builds&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In Christine's vision, completed around 1405, the goddesses Reason, Rectitude and Justice all confer with the author to construct a new city populated by the virtuous women of history. Armed with the 'spade of intelligence' Christine sets to work to build the city, telling the stories of righteous women while building the walls, houses, and appointing a queen to rule them all.&lt;/p&gt;

&lt;p&gt;These positive examplars of women of noble character continue the biblical tradition of providing instructive stories - and we can use this late medieval vision to provide a roadmap into something that I'm proud to launch as of today: &lt;strong&gt;a new nonprofit initiative that I'm calling &lt;a href="https://heraistudio.org/" rel="noopener noreferrer"&gt;Her AI Studio&lt;/a&gt;&lt;/strong&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%2Fwmm94d18fikyd9qa6ncz.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%2Fwmm94d18fikyd9qa6ncz.png" alt="Her AI Studio" width="500" height="643"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://heraistudio.org" rel="noopener noreferrer"&gt;https://heraistudio.org&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As an experienced nonprofit and educational founder (Wellesley Chinese Language School Co-Founder, Vue Vixens, Inc. Founder and CEO, &lt;a href="https://ff-school.jenlooper.com/" rel="noopener noreferrer"&gt;Front-End Foxes School&lt;/a&gt; Founder and CEO, &lt;a href="https://beanpotstudio.com/" rel="noopener noreferrer"&gt;Beanpot Studio&lt;/a&gt; CEO) I know what it takes to launch nonprofits and keep them going through funding and mission challenges (hello COVID). So what will we build, exactly?&lt;/p&gt;

&lt;p&gt;I'm inspired with a conversation with my daughter about the new interest in Cyber decks that's causing a renewed grass roots community to build hardware projects. I want this nonprofit organization to go global, opening cohorts for high-school aged students who identify as women mentored by college-aged students (and older) who will get girls building, fast. Staying on device, offline, and local, we will learn about protecting data, building ethically with locally-hosted models, and conserving our energy. We will focus on a four-beat cadence:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Beginners: Discover - learn about AI by discovering the women who influenced and built the field, while building your first locally powered AI app.&lt;/li&gt;
&lt;li&gt;Intermediate: Create - build a local AI project bridging hardware and software focusing on image, text, and voice.&lt;/li&gt;
&lt;li&gt;Advanced: Engineer - build your cyber deck with your own kit, learning to program with AI and using AI to program.&lt;/li&gt;
&lt;li&gt;Capstone: Showcase your project in international celebrations - you're now ready to pay it forward!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What are we looking for?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Interest&lt;/strong&gt; - do you want to mentor? would you like to join me in crafting curriculum? are you a teacher curious about the initiative, or a student who'd like to potentially join? Sign the interest form at heraistudio.org.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Funding&lt;/strong&gt; - we can't go it alone. Each student kit is going to cost about $250 - we are looking for sponsors. Please reach out!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expertise&lt;/strong&gt; - I've been working with hardware for years but chatting with you helps me stay up to date. I'd love to chat and learn your perspective on building with this model.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Join our Discord: &lt;a href="https://discord.gg/jRkR2t4UpC" rel="noopener noreferrer"&gt;https://discord.gg/jRkR2t4UpC&lt;/a&gt; and let's chat.&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%2Fau7kfc3tayyegrq345bq.jpg" 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%2Fau7kfc3tayyegrq345bq.jpg" alt="Get in, losers, we're gonna build a nonprofit" width="369" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Get in, losers, we're gonna build a nonprofit&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ai</category>
      <category>wecoded</category>
      <category>programming</category>
      <category>iot</category>
    </item>
    <item>
      <title>Say It With Flowers: BouqAIs with the Tussie-Mussie Generator</title>
      <dc:creator>Jen Looper</dc:creator>
      <pubDate>Sat, 09 May 2026 17:19:01 +0000</pubDate>
      <link>https://dev.to/cloudinary/say-it-with-flowers-bouqais-with-the-tussie-mussie-generator-3do0</link>
      <guid>https://dev.to/cloudinary/say-it-with-flowers-bouqais-with-the-tussie-mussie-generator-3do0</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;The &lt;strong&gt;&lt;a href="https://tussie-mussies.netlify.app" rel="noopener noreferrer"&gt;Tussie-Mussie Generator&lt;/a&gt;&lt;/strong&gt; is a web app for anyone who wants to send a meaningful floral e-card with an AI-generated "tussie mussie", a type of small nosegay. It uses the Victorian language of flowers to compose a bouquet, generates an image with Gemini, and sends it via Resend. This post covers &lt;br&gt;
a Mother's Day relaunch: fixing a broken email integration and swapping the AI image model.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What changed in this relaunch
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Migrated email sending from Mailgun to Resend, including a Cloudflare serverless function&lt;/li&gt;
&lt;li&gt;Fixed DNS configuration across Porkbun, Netlify, and Resend for a custom subdomain&lt;/li&gt;
&lt;li&gt;Replaced the previous image generator with Gemini for accurate tussie mussie generation&lt;/li&gt;
&lt;li&gt;Refactored the UI with Astro + Vue and impeccable.style for AI-assisted critique&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Have you ever wanted to build an e-card system that would allow you to send little ai-generated bouquets to your friends? No? Yes? Read on, gentle reader!&lt;/p&gt;

&lt;p&gt;Last year, I onboarded to my new job by building this sample app that lets you use the Victorian language of flowers to compose a bouquet. An image-heavy app like this is a great candidate for hosting and optimizing via Cloudinary, so all the flower images are stored there. An image of the composed bouquet is generated by Gemini, and then sent via Resend to the recipient of your choice. The app looks like this:&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%2Fj3w0zh47qr8wxul07kfk.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%2Fj3w0zh47qr8wxul07kfk.png" alt="Tussie Mussie Generator" width="800" height="722"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and the generated e-card looks like this:&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%2F9hl2hu7bcummpftq8k7x.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%2F9hl2hu7bcummpftq8k7x.png" alt="demo" width="800" height="1245"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I launched this app a year ago, and it's had an interesting lifespan. I wanted to relaunch it for Mother's Day and realized there was some refactoring to do! &lt;/p&gt;

&lt;p&gt;Here are a few of the gotchas I dealt with:&lt;/p&gt;

&lt;p&gt;Keeping the core: The basis of this app is its dataset, which I created a while ago on &lt;a href="https://www.kaggle.com/datasets/jenlooper/language-of-flowers" rel="noopener noreferrer"&gt;Kaggle&lt;/a&gt; as a useful version of the Victorian "Language of Flowers", scraped from the &lt;a href="https://www.almanac.com/flower-meanings-language-flowers#flower-meanings" rel="noopener noreferrer"&gt;Farmer's Almanac&lt;/a&gt;. Here's an example of what that looks like:&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%2Fz5l8fjmwlta0yy1trhoa.jpg" 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%2Fz5l8fjmwlta0yy1trhoa.jpg" alt="Almanac version of florilege" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If your idea is centered around a solid dataset, you can't go wrong. Once I had the list, it took me a while to scrape images of the flowers in question and store them, optimized, on Cloudinary. But that work was done a year ago. So two things remained for the relaunch: refactoring the UI and fixing the bits that stopped working, namely the e-card generator.&lt;/p&gt;

&lt;h2&gt;
  
  
  Refactoring the UI
&lt;/h2&gt;

&lt;p&gt;This app is built using my preferred stack, namely Astro + Vue. After a year, it was definitely time to upgrade, including fixing the tailwind integration which relied on an outdated package. Cursor made short work of that - it's one of the best uses of AI software agents.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tip: periodically ask your AI to plan the removal of any redundant files or code while you complete refactors. There are usually vestiges that can be deleted.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I used a wildly useful AI skill to refactor my admittedly basic UI: &lt;a href="https://impeccable.style/" rel="noopener noreferrer"&gt;impeccable.style&lt;/a&gt;. Install these skills on your machine and as you work with any AI generated UI suggestions, run &lt;code&gt;/impeccable critique&lt;/code&gt; to reality-check your work for taste and style. I'm now using impeccable on all my UIs - it also helped me tidy &lt;a href="https://jenlooper.com" rel="noopener noreferrer"&gt;my portfolio&lt;/a&gt;. The UI went from this: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx6luq0346nkg7qkrvbv2.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx6luq0346nkg7qkrvbv2.webp" alt="old UI" width="800" height="638"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;...to the UI shown above. I like it a lot. &lt;/p&gt;




&lt;h2&gt;
  
  
  Fixing the E-Card functionality
&lt;/h2&gt;

&lt;p&gt;This app caused me a horrible panic after launch last year, ruining an anniversary dinner, due to a kerfuffle with Mailgun, the original e-card sending mechanism. I was shocked by an enormous overage charge due to hacking of their systems, and I shut the entire thing down as fast as I could. To relaunch, I needed to find a new service to send email via API. Once again, Cursor to the rescue. &lt;/p&gt;

&lt;p&gt;Enter &lt;a href="https://resend.com/" rel="noopener noreferrer"&gt;Resend&lt;/a&gt;, a new email service that is much more solid than my previous choice. Let us hope that we have smooth sailing from now on! The integration includes a serverless function that you can host on Cloudflare, and works well as soon as you verify a domain. That last part took me the longest time - the gotcha is that since I bought the domain on Porkbun, I had to edit DNS records there to serve emails from a subdomain. The site is hosted on Netlify, so I had to build the subdomain there, and then the DNS verified on Resend. It's the kind of task that you don't do often, so it's easy to miss a step. But all is set now, and the emails are sending!&lt;/p&gt;

&lt;p&gt;I also had to reconsider how to build the AI generated bouquets. I'm out of credits on the service I previously chose to generate the bouquets, and they were never quite right anyway (generated as standard bouquets rather than true tussie mussies which have a silver filigree holder). It's crazy to see how AI has evolved for this esoteric imagery over the past year - now Gemini can accurately compose a tussie mussie in its holder, given a specific prompt.&lt;/p&gt;

&lt;p&gt;Enjoy these little bouquets! &lt;a href="https://tussie-mussies.netlify.app/" rel="noopener noreferrer"&gt;Send one to your Mom today&lt;/a&gt;!&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Cloudinary ❤️ developers&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ready to level up your media workflow? Start using Cloudinary for free and build better visual experiences today.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👉 &lt;strong&gt;&lt;a href="https://link.cloudinary.com/uoTiK" rel="noopener noreferrer"&gt;Create your free account&lt;/a&gt;&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>ai</category>
      <category>gemini</category>
      <category>astro</category>
      <category>vue</category>
    </item>
    <item>
      <title>How to Win a Hackathon</title>
      <dc:creator>Jen Looper</dc:creator>
      <pubDate>Sun, 03 May 2026 03:43:08 +0000</pubDate>
      <link>https://dev.to/cloudinary/how-to-win-a-hackathon-1377</link>
      <guid>https://dev.to/cloudinary/how-to-win-a-hackathon-1377</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;A sponsor’s perspective&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This blogpost is written on the heels of a few amazing weekends spent knee-deep in students in Toronto and Los Angeles at Hack Canada and LA Hacks, engaging almost 2000 students in person in total. Managing a sponsor track, I have some thoughts on good ways to present yourselves when angling your project towards a sponsor prize.&lt;/p&gt;

&lt;p&gt;For those not as familiar with how student hackathons work, they are usually comprised of an organized weekend populated by college undergrads (or older, or younger! high schoolers are increasingly getting into the mix). Over about 36 hours, students form teams and try to build software that meets a need, solves a problem, or proves a point. Some hackathons focus on themes around social good, like Hack Canada which challenged students to build something of special interest to Canada. Others have several themes and a full roster of sponsored options. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hackathons are a bastion of the cozy web! It’s a delight to see students embracing the freedom to create weird, useless, and wildly creative apps like AeroMaxx, a way to gauge influencers’ aerodynamic-maxxing (Taylor Swift is more aerodynamic than Clavicular, did you know?). LA Hacks even had a track embracing the “most questionable” hack, won by “Yes? Or Yes!”, a hack that had literal goldfish helping make decisions and trigger AI agents to do things like break up with a girl friend, quit a job, and post to social media. Great projects. Please keep building this kind of weird stuff.&lt;/p&gt;
&lt;/blockquote&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%2Fgn9ghxj1wt5pctpuhve0.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%2Fgn9ghxj1wt5pctpuhve0.png" alt="MagByte" width="799" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;a cozy interface for roommates to manage their shopping lists: &lt;a href="https://devpost.com/software/magbite?_gl=1*1nkfo64*_gcl_au*MjA0ODI2ODI1OC4xNzcyOTAyNTc4*_ga*Mzk1NTQ4NjAwLjE3NzI5MDI1Nzk.*_ga_0YHJK3Y10M*czE3Nzc3Nzc1MDMkbzM4JGcxJHQxNzc3Nzc3NjM5JGo0OCRsMCRoMA.." rel="noopener noreferrer"&gt;MagByte&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s a great moment to let your creativity run wild:&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%2Fi55ce5tfipfpjajllj59.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%2Fi55ce5tfipfpjajllj59.png" alt="goldfish q&amp;amp;a" width="800" height="568"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://devpost.com/software/yes-or-yes?_gl=1*nv0asj*_gcl_au*MjA0ODI2ODI1OC4xNzcyOTAyNTc4*_ga*Mzk1NTQ4NjAwLjE3NzI5MDI1Nzk.*_ga_0YHJK3Y10M*czE3Nzc3Nzc1MDMkbzM4JGcxJHQxNzc3Nzc4MTA2JGo1NiRsMCRoMA.." rel="noopener noreferrer"&gt;Yes? Or Yes!&lt;/a&gt; LA Hacks most unhinged hack winner - goldfish help you make decisions and then trigger agentic workflows. Good luck!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’ve been working the hackathon circuit as a developer advocate for at least ten years, including the mess during COVID, and the major difference nowadays between the earlier events is, unsurprisingly, the heavy use of AI tooling that both speeds up delivery, polishes the final product, and gets more software shipped, way faster. Instead of watching demo videos, judges can actively interact with an almost production-ready app, shockingly quickly.&lt;/p&gt;

&lt;p&gt;Still, there are some tips and tricks that I’d like to share based on a few recent observations of these very fun events. Here are five things to keep in mind as you go for a win!&lt;/p&gt;

&lt;h2&gt;
  
  
  Who’s it for?
&lt;/h2&gt;

&lt;p&gt;A lot of folks are attracted to social-good type projects, and that’s very laudable. I love them, personally, and tend to mark them highly in my judging rubric. I just want to caution hackathon participants against going after this vector with a “tech savior complex”. It’s easy to think that tech can solve all the things. And tech has made great strides on devices such as most smart phones to help people with various accessibility issues such as those who are blind, color blind, experience hearing loss, or are mobility-challenged. &lt;/p&gt;

&lt;p&gt;But if you are going to create a project for a given sector of users, it’s critical to come at these projects with an open mindset and humility, and to work with people who have these actual challenges. Working on an app for older people? Call your Grandma or Grandpa before you start building. It’s likely they can guide you to refining your app to make it much more useful.&lt;/p&gt;

&lt;p&gt;At Hack Canada, teams succeeded who spent time talking to local security guards as they built, to ensure that their safety-oriented hack would be useful. It’s all about finding your customer and/or user, and building backwards from that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Power to the People
&lt;/h2&gt;

&lt;p&gt;Sponsors and mentors are often available to help you all the way through the event, so make use of them! Take some time before you start working to talk to sponsors and see if they can help you start to ideate. Learn about the various sponsors and what they are looking for. Some are looking for product feedback, some are hiring, others may have another agenda. &lt;/p&gt;

&lt;p&gt;Maybe you’ll find a product that you never heard of and will make a nice professional connection. Get your LinkedIn app ready to scan codes! This is a great moment to network.&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%2F82424uizvh1gzcbpfr3z.jpg" 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%2F82424uizvh1gzcbpfr3z.jpg" alt="Jen and Raya" width="582" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;At your service at the booth!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Skill Up
&lt;/h2&gt;

&lt;p&gt;The use of AI tools has accelerated the build phase of a hackathon almost beyond recognition. When I was judging hackathons around 2016 or so, the projects produced were super shaky - often the hacker created a video at the moment that the app worked, and showed that to a judge as it was highly unlikely they could ever get it working again! This has all changed when AI has become such an important build partner.&lt;/p&gt;

&lt;p&gt;The use of AI, however, shifts the focus of a hackathon away from the software engineering tasks that now can be offloaded to agents (with supervision, of course!). Now, you need to spend a good amount of time getting set up to make your AI as successful and efficient as you can. Many vendors have created tools to help; we at Cloudinary produced a Skills Pack for this purpose: &lt;a href="https://github.com/cloudinary-devs/skills" rel="noopener noreferrer"&gt;https://github.com/cloudinary-devs/skills&lt;/a&gt;. Using tools such as these will effectively give official context to your app, preventing hallucinations and other wasting of time. &lt;/p&gt;

&lt;h2&gt;
  
  
  Sponsormaxxing and platformmaxxing
&lt;/h2&gt;

&lt;p&gt;There’s a trend towards “sponsormaxxing” - LA Hacks even had a track for this. The term refers to the incorporation of as many sponsors’ platforms as possible in a given hack. I think it’s a fun challenge, but there’s a risk: if you want to win a sponsor track, you’ll likely be judged on deep integration with their platform. &lt;/p&gt;

&lt;p&gt;In our case at Cloudinary, I’m looking for not just the use of the platform as a place to dump images and video, but a deep use of the APIs, for example optimizing and transforming images in an AI pipeline with the goal of constructing videos (as you can do with our winning hack from LA Hacks, StudyO!). Going for a sponsor track prize? Focus on deep integrations.&lt;/p&gt;

&lt;p&gt;Going deep, however, can also be risky! There’s the risk of “platformmaxxing” - using every bell and whistle of a platform to try it out. You may need to be a bit strategic here, as you might max out your credits by piling on as many platform aspects as you can. Watch out for signs of maxing out your credits and plan accordingly.&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%2Folgg9609kdf96demfwx8.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%2Folgg9609kdf96demfwx8.png" alt="AeroMaxx" width="800" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Speaking of maxxing: &lt;a href="https://devpost.com/software/aeromaxx?_gl=1*vb7ib8*_gcl_au*MjA0ODI2ODI1OC4xNzcyOTAyNTc4*_ga*Mzk1NTQ4NjAwLjE3NzI5MDI1Nzk.*_ga_0YHJK3Y10M*czE3Nzc3Nzc1MDMkbzM4JGcxJHQxNzc3Nzc3OTE0JGoxNiRsMCRoMA.." rel="noopener noreferrer"&gt;AeroMaxx&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Perfect the Pitch
&lt;/h2&gt;

&lt;p&gt;Judges have only a few minutes per team so you need to make your best impression right away. Come ready to demo with your storytelling skills well in hand. Do some trial runs prior, and get feedback on your presentation skills. Lead with the use case, show the demo, and field questions. A good strategy for a 2-3 minute hackathon pitch is to focus on the problem you see, the solution you propose, and the impact you predict. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hook (15 seconds): Introduce yourself, state the problem you are solving, and share a relatable scenario or story.&lt;/li&gt;
&lt;li&gt;Demo (60-90 seconds): Walk the audience through your core feature&lt;/li&gt;
&lt;li&gt;Tech (30 seconds): Briefly explain the most impressive technical hurdle you overcame or the tech stack that powers the hack.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Tip: Consider recording a video of your demo just in case there’s a horrible glitch, and practice getting over a glitchy demo. You can do it!&lt;/p&gt;
&lt;/blockquote&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%2Fvfusg6w07felvou9vje9.jpg" 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%2Fvfusg6w07felvou9vje9.jpg" alt="pitch me" width="799" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pitching can be intense!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Winning a hackathon is a great goal, but don’t forget that being accepted into some of these selective hackathons, participating, and building a piece of software that you can be proud of is very laudable as well. Wishing you the best of luck! And maybe we will see you there.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Cloudinary ❤️ developers&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ready to level up your media workflow? Start using Cloudinary for free and build better visual experiences today.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👉 &lt;strong&gt;&lt;a href="https://link.cloudinary.com/uoBac" rel="noopener noreferrer"&gt;Create your free account&lt;/a&gt;&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>career</category>
      <category>community</category>
      <category>programming</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>6 Free Cloudinary Courses That Teach You to Build Media-Optimized Apps</title>
      <dc:creator>Jen Looper</dc:creator>
      <pubDate>Thu, 30 Apr 2026 18:33:51 +0000</pubDate>
      <link>https://dev.to/cloudinary/6-free-cloudinary-courses-that-teach-you-to-build-media-optimized-apps-51m2</link>
      <guid>https://dev.to/cloudinary/6-free-cloudinary-courses-that-teach-you-to-build-media-optimized-apps-51m2</guid>
      <description>&lt;p&gt;The Cloud to Crowd (C2C) curriculum is Cloudinary's free 6-course learning suite for developers who want to build and ship applications with production-quality image and video handling. It covers everything from Cloudinary basics to AI-powered transformations paired with hands-on projects at every step and a free certificate when you complete each course.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Fun fact: community member Jerome Hardaway of Vets Who Code, one of our nonprofit partners in the Creators Community program, coined the "Cloud to Crowd" term!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What You'll Be Able to Build After This Curriculum
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Upload, manage, and transform media assets using the Cloudinary API&lt;/li&gt;
&lt;li&gt;Optimize images and video for performance in a Next.js app&lt;/li&gt;
&lt;li&gt;Apply AI-powered features: background removal, generative fill, auto-tagging&lt;/li&gt;
&lt;li&gt;Use the Python SDK to automate media workflows on the backend&lt;/li&gt;
&lt;li&gt;Deliver video efficiently with format and quality auto-selection&lt;/li&gt;
&lt;li&gt;Ship a real portfolio project that demonstrates all of the above&lt;/li&gt;
&lt;li&gt;Earn a Cloudinary certificate and qualify for the Creators Community&lt;/li&gt;
&lt;/ul&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%2Fevy6pbfnmkb347ezgdgs.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%2Fevy6pbfnmkb347ezgdgs.png" alt="Cloudinary Academy Courseware" width="800" height="577"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why We Built This
&lt;/h2&gt;

&lt;p&gt;Getting images and video right in a production app is harder than most tutorials suggest. Resize, format, optimize, deliver — each step has tradeoffs, and most learning resources skip the messy middle. The C2C courses are designed to fill that gap: structured, project-driven, and free.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 6 Free Courses
&lt;/h2&gt;

&lt;p&gt;All courses are self-paced and free at &lt;a href="https://training.cloudinary.com/pages/c2c" rel="noopener noreferrer"&gt;https://training.cloudinary.com/pages/c2c&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Course 1: Media IQ for Developers
&lt;/h3&gt;

&lt;p&gt;Enroll: &lt;a href="https://training.cloudinary.com/learn/course/devrel-c2c-intro" rel="noopener noreferrer"&gt;https://training.cloudinary.com/learn/course/devrel-c2c-intro&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The starting point for the first three courses in this suite. This course covers the core Cloudinary concepts: cloud name, API keys, the Media Library, and your first upload and transformation. No prior Cloudinary experience needed. By the end, you'll have a free account set up and understand how assets move through the Cloudinary pipeline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You'll learn to:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Choose the right image formats for your use case, understanding when to use JPEG, PNG, WebP, AVIF, or SVG based on content type, browser support, and performance trade-offs&lt;/li&gt;
&lt;li&gt;Apply strategic optimization techniques including responsive images with automatic format/quality selection and intelligent cropping with gravity detection&lt;/li&gt;
&lt;/ul&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%2Fry0mgv63008yznpxadxa.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%2Fry0mgv63008yznpxadxa.png" alt="Heavy Watch image" width="800" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Course 2: Media IQ for Developers with Next.js
&lt;/h3&gt;

&lt;p&gt;Enroll: &lt;a href="https://training.cloudinary.com/learn/course/devrel-c2c-next" rel="noopener noreferrer"&gt;https://training.cloudinary.com/learn/course/devrel-c2c-next&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The flagship course of the C2C curriculum. You'll build a storefront application (the "CapZone" e-commerce store) using Next.js and the Cloudinary Next.js SDK, applying real-world image optimization patterns throughout.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You'll learn to:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build an image-optimized web application using Cloudinary to upload, store, transform, and deliver images on-the-fly through URL-based transformations and CDN delivery&lt;/li&gt;
&lt;li&gt;Apply strategic optimization techniques including responsive images with automatic format/quality selection and intelligent cropping with gravity detection&lt;/li&gt;
&lt;li&gt;Create Next.js components that handle image uploading, display optimized images with overlays and watermarks, and serve different sizes for different contexts&lt;/li&gt;
&lt;/ul&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%2Fhhpe6j2tmei2fq4ao40v.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%2Fhhpe6j2tmei2fq4ao40v.png" alt="CapZone store" width="800" height="676"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The kind of component you'll be writing by the end of this course:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&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;CldImage&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="s1"&gt;next-cloudinary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProductImage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;publicId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;alt&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;publicId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CldImage&lt;/span&gt;
      &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;publicId&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;crop&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"fill"&lt;/span&gt;
      &lt;span class="na"&gt;gravity&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"auto"&lt;/span&gt;
      &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"auto"&lt;/span&gt;
      &lt;span class="na"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"auto"&lt;/span&gt;
      &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;/&amp;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;h3&gt;
  
  
  Course 3: Media IQ for Developers with AI
&lt;/h3&gt;

&lt;p&gt;Enroll: &lt;a href="https://training.cloudinary.com/learn/course/devrel-c2c-ai" rel="noopener noreferrer"&gt;https://training.cloudinary.com/learn/course/devrel-c2c-ai&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cloudinary's AI-powered transformation features are some of its most practical and useful. This course covers the tools that save the most time in real projects: background removal, generative fill, object-aware cropping, and auto-tagging. This course follows the Next.js course, building AI into the CapZone store by adding a color picker to colorize the hats.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You'll learn to:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Remove or replace backgrounds using e_background_removal&lt;/li&gt;
&lt;li&gt;Use generative fill to extend or recompose images&lt;/li&gt;
&lt;li&gt;Apply smart cropping with gravity: "auto" for face- and object-aware results&lt;/li&gt;
&lt;li&gt;Auto-tag assets using AI analysis to make large libraries searchable&lt;/li&gt;
&lt;/ul&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%2Fb4ryvwuokux6f54ai35k.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%2Fb4ryvwuokux6f54ai35k.png" alt="AI CapZone" width="800" height="811"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Course 4: Media IQ for Developers with Video
&lt;/h3&gt;

&lt;p&gt;Enroll: &lt;a href="https://training.cloudinary.com/learn/course/devrel-c2c-video" rel="noopener noreferrer"&gt;https://training.cloudinary.com/learn/course/devrel-c2c-video&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Video is a different beast from images; you manage larger files, adaptive bitrates, more delivery complexity. This course covers the Cloudinary video pipeline from upload to playback. It's the third and final part of the progressive enhancement of the CapZone e-commerce web app that you started prior. In this course you'll add a video to one of the products.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You'll learn to:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upload and transcode video using the Cloudinary API&lt;/li&gt;
&lt;li&gt;Apply video transformations: trim, resize, overlay, add subtitles&lt;/li&gt;
&lt;li&gt;Build playlists and seekbars with subtitles&lt;/li&gt;
&lt;/ul&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%2F2xo1znkfbl48ywp9zu6d.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%2F2xo1znkfbl48ywp9zu6d.png" alt="Video with playlist on CapZone" width="800" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Course 5: Media IQ for Developers with Python
&lt;/h2&gt;

&lt;p&gt;For less JavaScript-focused developers, this course covers Cloudinary's Python SDK: uploading, managing, and transforming assets from any Python environment.&lt;br&gt;
Enroll: &lt;a href="https://training.cloudinary.com/learn/course/devrel-c2c-python" rel="noopener noreferrer"&gt;https://training.cloudinary.com/learn/course/devrel-c2c-python&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You'll learn to:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authenticate and configure the Python SDK&lt;/li&gt;
&lt;li&gt;Upload and tag assets for organization&lt;/li&gt;
&lt;li&gt;Build a small pet store app&lt;/li&gt;
&lt;/ul&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%2Fq1f7brwa80h9bjk2p7yy.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%2Fq1f7brwa80h9bjk2p7yy.png" alt="pet store app" width="800" height="542"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Course 6: Building a Developer Portfolio with Cloudinary and Next.js
&lt;/h2&gt;

&lt;p&gt;Enroll: &lt;a href="https://training.cloudinary.com/learn/course/devrel-c2c-portfolio" rel="noopener noreferrer"&gt;https://training.cloudinary.com/learn/course/devrel-c2c-portfolio&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The capstone course. You'll apply everything from the earlier modules: upload, transformation, optimization, and AI features — to build a complete portfolio site using React and TypeScript that demonstrates your Cloudinary skills to employers. Focus on your own skills and show them off to the world in a media-rich website.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You'll learn to:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Architect a media-rich portfolio app from scratch&lt;/li&gt;
&lt;li&gt;Combine multiple Cloudinary transformations into a polished UI&lt;/li&gt;
&lt;li&gt;Optimize for performance: lazy loading, responsive breakpoints, format auto-selection&lt;/li&gt;
&lt;li&gt;Deploy a production-ready app you can link from your resume&lt;/li&gt;
&lt;/ul&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%2F4891ntoh6m30xoqdmsvh.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%2F4891ntoh6m30xoqdmsvh.png" alt="portfolio" width="800" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Who's this for?
&lt;/h2&gt;

&lt;p&gt;The C2C curriculum works for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Early-career developers who want structured, project-based learning beyond "hello world" tutorials&lt;/li&gt;
&lt;li&gt;Career changers building a portfolio they can point to during job interviews&lt;/li&gt;
&lt;li&gt;Frontend developers who've used Cloudinary but want to understand the optimization and AI layers&lt;/li&gt;
&lt;li&gt;Backend developers looking to add media handling to their Python projects&lt;/li&gt;
&lt;li&gt;Bootcamp graduates who want to go from tutorial-follower to someone who can build production media pipelines&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Enroll for Free
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to training.cloudinary.com/pages/c2c&lt;/li&gt;
&lt;li&gt;Create a free Cloudinary Academy account — you just need an email address&lt;/li&gt;
&lt;li&gt;Start with Course 1 or jump to the course that matches your current skill level&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All 6 courses are free. Each comes with a completion certificate. Finishing any course and passing its final assessment at 80%+ earns you a credential that qualifies you to apply to the Cloudinary Creators Community, a network with mentorship, Discord access, mini-hack events, and connections to developers worldwide.&lt;/p&gt;

&lt;h2&gt;
  
  
  About the Cloudinary Creators Community
&lt;/h2&gt;

&lt;p&gt;The Cloudinary Creators Community (CCC) is a selective, cohort-based program for developers who've completed the C2C curriculum. &lt;/p&gt;

&lt;p&gt;It includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access to private Discord channels with Cloudinary's Developer Relations team&lt;/li&gt;
&lt;li&gt;Monthly mini-hack challenges to apply and extend your skills&lt;/li&gt;
&lt;li&gt;Peer review and mentorship from a global network of developers&lt;/li&gt;
&lt;li&gt;Badges, certificates, and holopins with social media shout-outs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can join the Discord server now and participate in community activities even while you're working through the curriculum.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Ready to start? → &lt;a href="//training.cloudinary.com/pages/c2c"&gt;training.cloudinary.com/pages/c2c&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This blogpost was crafted with the help of Claude. Banner image by Nano Banana.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Cloudinary ❤️ developers&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ready to level up your media workflow? Start using Cloudinary for free and build better visual experiences today.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👉 &lt;strong&gt;&lt;a href="https://link.cloudinary.com/un3am" rel="noopener noreferrer"&gt;Create your free account&lt;/a&gt;&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
