<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
        <title><![CDATA[Eliseo Martelli]]></title>
        <description><![CDATA[Eliseo Martelli]]></description>
        <link>https://eliseomartelli.it</link>
        <generator>RSS for Node</generator>
        <lastBuildDate>Sun, 10 May 2026 04:16:30 GMT</lastBuildDate>
        <atom:link href="https://eliseomartelli.it/feed.xml" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Logbook - May 7, 2026]]></title>
            <description><![CDATA[<i>Posted on: <span><a href="https://eliseomartelli.it/blog/tags/logbook">Logbook</a>, </span><span><a href="https://eliseomartelli.it/blog/tags/misc">Misc</a>.</span></i><p>Tagline: Pages from my logbook.</p><hr/><p>Yesterday morning I watched a puddle drying up, taken by force by the sun&#x27;s
rays.<br/>
A &quot;necessary&quot; violence to make room for the next puddle.</p>
<p>Transformation is an act of rupture, and at this moment, I feel I am in a phase
of transformation.</p>
<p>Today I feel a bit like that puddle: a bit mortified, a bit disappointed, and a
bit of a disappointment.</p>
<p>Does evaporating mean failing?</p>]]></description>
            <link>https://eliseomartelli.it/blog/2026-05-07-logbook-may-7-2026</link>
            <guid isPermaLink="true">https://eliseomartelli.it/blog/2026-05-07-logbook-may-7-2026</guid>
            <category><![CDATA[Logbook]]></category>
            <category><![CDATA[Misc]]></category>
            <pubDate>Thu, 07 May 2026 09:05:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[AirPods Max 2: an infuriating love letter]]></title>
            <description><![CDATA[<i>Posted on: <span><a href="https://eliseomartelli.it/blog/tags/apple">Apple</a>, </span><span><a href="https://eliseomartelli.it/blog/tags/review">Review</a>.</span></i><p>Tagline: A brutally honest review of Apple&#x27;s premium headphones</p><hr/><p>You&#x27;ve probably read four hundred seventeen reviews of the AirPods Max 2
already. I&#x27;m not here to tell you they&#x27;re &quot;life-changing&quot; or wax poetic about
unboxing experiences. I&#x27;m a critical Apple fan with a collection of decent IEMs
and headphones, and I have strong opinions about where these fit in the audio
landscape.</p>
<p>Let&#x27;s address the elephant in the room first: they cost a ridiculous amount of
money for a consumer headphone. €579 / $549 is not a casual purchase. So are
they worth it?</p>
<h2>The Noise Cancellation</h2>
<p>Comparing the ANC to the original AirPods Max, Apple&#x27;s claim of &quot;1.5x better&quot;
is the kind of vague marketing figure Apple loves.
In practice? It works. The low-end engine hum and midrange chatter of a crowded
place genuinely fade out better than they did in 2020. You can hear voices are
more muffled in a way that actually registers.</p>
<p>Is it the best ANC out there? No. If you ask me, the original AirPods Pro 1
still had the most natural implementation of ANC Apple ever shipped. The Max 2
might be technically superior at blocking decibels, but the Pro 1 felt less
pressurized and more balanced.</p>
<h2>Transparency Mode</h2>
<p>This is where Apple usually left everyone else in the dust, but IMHO AirPods
Max 2 feel a bit metallic.</p>
<p>It&#x27;s something I also notice on the original AirPods Max and the new AirPods
Pro 3. Transparency Mode is clinical, and it lacks that &quot;ear-opening&quot;
naturalism of the past. To my ears, the AirPods Pro 1 still hold the crown for
the most convincing transparency. They’re the only ones that truly made me
forget I was wearing anything at all.</p>
<h2>The Sound</h2>
<p>Compared to the AirPods Max 1, the bass is richer without muddying the mids.
Instrument separation is surprisingly precise for a consumer Bluetooth
headphone. Listen to well-mastered lossless tracks and you&#x27;ll notice a wider
soundstage than most Bluetooth gear has any right to claim.</p>
<p>But here&#x27;s my biggest gripe: Apple still won&#x27;t give us a proper EQ.</p>
<p>We get &quot;Adaptive EQ&quot; that allegedly tailors to your ear shape. For €579, I want
to tweak the frequency response myself. Is that really too much to ask in 2026?</p>
<div class="not-prose my-6 grid grid-cols-1 sm:grid-cols-2 gap-4"><div class="rounded-xl border border-emerald-200 bg-emerald-50 dark:border-emerald-900 dark:bg-emerald-950/30 p-4"><p class="font-semibold text-emerald-800 dark:text-emerald-300 mb-3">Pros</p><ul class="space-y-2"><li class="flex items-start gap-2 text-sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check mt-0.5 size-4 shrink-0 text-emerald-600 dark:text-emerald-400" aria-hidden="true"><path d="M20 6 9 17l-5-5"></path></svg><span>Bass response is clean and present without bleeding into mids</span></li><li class="flex items-start gap-2 text-sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check mt-0.5 size-4 shrink-0 text-emerald-600 dark:text-emerald-400" aria-hidden="true"><path d="M20 6 9 17l-5-5"></path></svg><span>Soundstage is wider than expected for Bluetooth headphones</span></li><li class="flex items-start gap-2 text-sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check mt-0.5 size-4 shrink-0 text-emerald-600 dark:text-emerald-400" aria-hidden="true"><path d="M20 6 9 17l-5-5"></path></svg><span>Lossless audio over USB-C when wired</span></li><li class="flex items-start gap-2 text-sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check mt-0.5 size-4 shrink-0 text-emerald-600 dark:text-emerald-400" aria-hidden="true"><path d="M20 6 9 17l-5-5"></path></svg><span>Adaptive Audio works well for handling sudden loud noises</span></li></ul></div><div class="rounded-xl border border-red-200 bg-red-50 dark:border-red-900 dark:bg-red-950/30 p-4"><p class="font-semibold text-red-800 dark:text-red-300 mb-3">Cons</p><ul class="space-y-2"><li class="flex items-start gap-2 text-sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x mt-0.5 size-4 shrink-0 text-red-600 dark:text-red-400" aria-hidden="true"><path d="M18 6 6 18"></path><path d="m6 6 12 12"></path></svg><span>No user-adjustable multi-band EQ</span></li><li class="flex items-start gap-2 text-sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x mt-0.5 size-4 shrink-0 text-red-600 dark:text-red-400" aria-hidden="true"><path d="M18 6 6 18"></path><path d="m6 6 12 12"></path></svg><span>Feels heavy, especially when doing activities / shores around the house</span></li><li class="flex items-start gap-2 text-sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x mt-0.5 size-4 shrink-0 text-red-600 dark:text-red-400" aria-hidden="true"><path d="M18 6 6 18"></path><path d="m6 6 12 12"></path></svg><span>That stupid &#x27;bra&#x27; case offers zero real protection</span></li><li class="flex items-start gap-2 text-sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x mt-0.5 size-4 shrink-0 text-red-600 dark:text-red-400" aria-hidden="true"><path d="M18 6 6 18"></path><path d="m6 6 12 12"></path></svg><span>Transparency mode feels metallic</span></li></ul></div></div>
<h2>The H2 Features</h2>
<p>The H2 chip actually delivers some new experiences. Here&#x27;s what actually
matters day-to-day:</p>
<p>Conversation Awareness: This feels like witchcraft. Start talking and your
music immediately lowers while the mics pipe in the person you&#x27;re speaking to.</p>
<p>Adaptive Audio: A middle ground between ANC and Transparency mode. It handles
sudden loud noises (sirens, car horns) while keeping you aware of your
surroundings.</p>
<p>Lossless via USB-C: The USB-C port supports ultra-low latency lossless audio
when wired.</p>
<h2>The Disappointments</h2>
<p>The case is still ridiculous. They kept the Smart Case. It offers zero
protection for the headband, collects dust, and looks like you&#x27;re carrying a
bra around. The material is as shitty as the iPad&#x27;s Magic Keyboard / Smart
Folio.</p>
<p>The weight hasn&#x27;t changed. If you suffered from crown fatigue or clamping
pressure with the original, the Max 2 won&#x27;t solve your problems. They&#x27;re still
heavy.</p>
<p>No power button. You still can&#x27;t just turn them off. You have to put them in
that stupid case to force deep sleep. It&#x27;s 2026 and this still bothers me.</p>
<h2>The Verdict</h2>
<div class="not-prose my-2 my-4"><div class="flex items-center justify-between mb-1"><span class="font-medium text-base">Sound Quality</span><span class="font-semibold tabular-nums text-base">8.5/10</span></div><div class="w-full rounded-full bg-zinc-200 dark:bg-zinc-700 h-3"><div class="rounded-full bg-emerald-500 h-3" style="width:85%"></div></div></div>
<div class="not-prose my-2 my-4"><div class="flex items-center justify-between mb-1"><span class="font-medium text-base">Noise Cancellation</span><span class="font-semibold tabular-nums text-base">8/10</span></div><div class="w-full rounded-full bg-zinc-200 dark:bg-zinc-700 h-3"><div class="rounded-full bg-emerald-500 h-3" style="width:80%"></div></div></div>
<div class="not-prose my-2 my-4"><div class="flex items-center justify-between mb-1"><span class="font-medium text-base">Build &amp; Comfort</span><span class="font-semibold tabular-nums text-base">7/10</span></div><div class="w-full rounded-full bg-zinc-200 dark:bg-zinc-700 h-3"><div class="rounded-full bg-amber-500 h-3" style="width:70%"></div></div></div>
<div class="not-prose my-2 my-4"><div class="flex items-center justify-between mb-1"><span class="font-medium text-base">Value for Money</span><span class="font-semibold tabular-nums text-base">6/10</span></div><div class="w-full rounded-full bg-zinc-200 dark:bg-zinc-700 h-3"><div class="rounded-full bg-amber-500 h-3" style="width:60%"></div></div></div>
<div class="not-prose my-2 my-4"><div class="flex items-center justify-between mb-1"><span class="font-medium text-base">Overall</span><span class="font-semibold tabular-nums text-base">7.5/10</span></div><div class="w-full rounded-full bg-zinc-200 dark:bg-zinc-700 h-3"><div class="rounded-full bg-amber-500 h-3" style="width:75%"></div></div></div>
<p>These are brilliant headphones if you fit the profile. But they&#x27;re infuriating
in ways Apple refuses to fix.</p>
<p>If you own the original AirPods Max: Skip the upgrade. Unless your battery is
completely dead or you need USB-C lossless, the audio bump isn&#x27;t worth the
massive upgrade fee.</p>
<p>If you own AirPods Pro 1, 2, 3: Keep them. The Pro 2 do 90% of what the Max 2
do for less than half the price. The only reason to switch is if you genuinely
need over-ear form factor.</p>
<p>If you&#x27;re buying your first premium over-ears: These are worth considering.
They sound incredible, ANC is solid, and they&#x27;ll easily last five to seven
years (I&#x27;m still rocking my AirPods Pro 1, for example). Just budget for a real
case and accept that Apple won&#x27;t let you touch the EQ settings for now.</p>
<h2>The Crash</h2>
<p>But here&#x27;s something worth mentioning: they crashed on me. Completely. Wouldn&#x27;t
play any audio, refused to respond to controls. The only way to get them
working again was a full reboot. It happened once during testing, and while
it&#x27;s not a pattern (yet), it&#x27;s worth noting for a €579 product. I&#x27;d expect
better stability from hardware at this price point.</p>
<p>You might also appreciate <a href="/blog/2025-03-02-apple-quality">my thoughts on Apple&#x27;s software
quality</a> and how their hardware sometimes masks
software corners they cut.</p>
<h2>Testing the Sound</h2>
<p>I put these through my usual testing playlist to see how the these headphones
handle different extremes:</p>
<h3>La Sera Dei Miracoli - Lucio Dalla</h3>
<p>Dalla&#x27;s classic is a sprawling, messy arrangement. I use this to see how well
headphones handle chaos, the piano, the backing vocals, and his soaring voice
all fighting for space.</p>
<iframe allow="autoplay *; encrypted-media *;" height="150" class="w-full max-w-2xl overflow-hidden bg-transparent" sandbox="allow-forms allow-popups allow-same-origin allow-scripts allow-storage-access-by-user-activation allow-top-navigation-by-user-activation" src="https://embed.music.apple.com/it/album/la-sera-dei-miracoli/253944002?i=253944008&amp;l=en-GB"></iframe>
<h3>Figli Delle Stelle - Alan Sorrenti</h3>
<p>I hate that I love this disco track. It’s perfect for testing high-end treble.
If the headphones are too sharp, the hi-hats and falsetto will pierce your
eardrums. Here, they sparkle nicely.</p>
<iframe allow="autoplay *; encrypted-media *;" height="150" class="w-full max-w-2xl overflow-hidden bg-transparent" sandbox="allow-forms allow-popups allow-same-origin allow-scripts allow-storage-access-by-user-activation allow-top-navigation-by-user-activation" src="https://embed.music.apple.com/it/album/figli-delle-stelle/713108424?i=713108454&amp;l=en-GB"></iframe>
<h3>Oh my god - Adele</h3>
<p>A solid pop track for testing vocal presence. The AirPods Max 2 put Adele right
in the center of the room, isolating her voice cleanly from the heavy bass
drops without muddying either.</p>
<iframe allow="autoplay *; encrypted-media *;" height="150" class="w-full max-w-2xl overflow-hidden bg-transparent" sandbox="allow-forms allow-popups allow-same-origin allow-scripts allow-storage-access-by-user-activation allow-top-navigation-by-user-activation" src="https://embed.music.apple.com/it/album/oh-my-god/1590035691?i=1590036031&amp;l=en-GB"></iframe>
<h3>Bury a Friend - Billie Eilish</h3>
<p>Eilish&#x27;s production is absurdly bass-heavy with ASMR-style vocal whispers. If
the sub-bass is too muddy, the track becomes unlistenable. The low-end
surprisingly tight here.</p>
<iframe allow="autoplay *; encrypted-media *;" height="150" class="w-full max-w-2xl overflow-hidden bg-transparent" sandbox="allow-forms allow-popups allow-same-origin allow-scripts allow-storage-access-by-user-activation allow-top-navigation-by-user-activation" src="https://embed.music.apple.com/it/album/bury-a-friend/1450695723?i=1450695881&amp;l=en-GB"></iframe>
<h3>Shape of My Heart - Sting</h3>
<p>Yes, it&#x27;s cliché, but that acoustic guitar riff and the delicate snare hits are
great for checking instrument separation. You can actually hear the fingers
sliding on the strings.</p>
<iframe allow="autoplay *; encrypted-media *;" height="150" class="w-full max-w-2xl overflow-hidden bg-transparent" sandbox="allow-forms allow-popups allow-same-origin allow-scripts allow-storage-access-by-user-activation allow-top-navigation-by-user-activation" src="https://embed.music.apple.com/it/album/shape-of-my-heart/1415203729?i=1415203739&amp;l=en-GB"></iframe>]]></description>
            <link>https://eliseomartelli.it/blog/2026-04-02-airpods-max-2-review</link>
            <guid isPermaLink="true">https://eliseomartelli.it/blog/2026-04-02-airpods-max-2-review</guid>
            <category><![CDATA[Apple]]></category>
            <category><![CDATA[Review]]></category>
            <pubDate>Thu, 02 Apr 2026 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[A Gaussian Splat Portrait on the Homepage]]></title>
            <description><![CDATA[<i>Posted on: <span><a href="https://eliseomartelli.it/blog/tags/programming">Programming</a>.</span></i><p>Tagline: Rendering a 3D portrait from a single photo using Gaussian Splatting, Canvas 2D, and zero dependencies.</p><hr/><p>I recently watched a video by Tsoding where he rendered a full 3D scene using
just a plain canvas 2D element, without any WebGL or Three.js.</p>
<p>The core of the video was a simple formula to calculate the position of each
pixel:</p>
<pre><code>given (x, y, z)

x&#x27; = y / z
y&#x27; = x / z
</code></pre>
<p>It tickled the part of my brain that loves graphics and it was enough to make
me want to try the same thing on my homepage portrait. I wanted to create a 3D
portrait that could be rotated and viewed from different angles, all using just
Canvas 2D and some math.</p>
<iframe src="https://www.youtube-nocookie.com/embed/qjWkNZ0SXfo" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowFullScreen="" class="mx-auto w-full aspect-video rounded-lg overflow-hidden"></iframe>
<p>Some months ago, I was playing around with Apple&#x27;s
<a href="https://github.com/apple/ml-sharp">ml-sharp</a>, which can
reconstruct a 3D point cloud from a single photo.</p>
<p>At that time, I didn&#x27;t have a use for it, but it seemed like the perfect tool
for this project.</p>
<p>This technique is called Gaussian Splatting.
It uses millions of semi-transparent 3D blobs, each with a position, opacity,
color, scale, and rotation. Render them back-to-front and you get surprisingly
good results, especially on faces.</p>
<p>One photo in. One <code>.ply</code> file out.</p>
<p>I ran ml-sharp on a portrait of me and got a <code>.ply</code> file with ~689,000
gaussians. Most of them were background noise. I loaded it into
<a href="https://superspl.at/editor">superspl.at/editor</a> and cropped everything that
wasn&#x27;t my face.</p>
<p>Still way too many points. And the raw <code>.ply</code> format is huge. None of that is
useful for a grayscale web viewer.</p>
<p>689k gaussians at 60+ bytes each isn&#x27;t something you ship over the wire. I
wrote a Python script to cut it down.</p>
<p>The core idea: not all gaussians matter equally. A tiny opaque one is a sharp
detail — an eye, a lip edge. A huge transparent one is blurry fill. Score by
<code>opacity / volume</code>, importance-sample down to 15k, and you keep what the eye
actually sees:</p>
<figure data-rehype-pretty-code-figure=""><pre style="background-color:#22272e;color:#adbac7" tabindex="0" data-language="python" data-theme="github-dark-dimmed"><code data-language="python" data-theme="github-dark-dimmed" style="display:grid"><span data-line=""><span style="color:#ADBAC7">volume </span><span style="color:#F47067">=</span><span style="color:#ADBAC7"> np.exp(s0 </span><span style="color:#F47067">+</span><span style="color:#ADBAC7"> s1 </span><span style="color:#F47067">+</span><span style="color:#ADBAC7"> s2)</span></span>
<span data-line=""><span style="color:#ADBAC7">importance </span><span style="color:#F47067">=</span><span style="color:#ADBAC7"> opacity </span><span style="color:#F47067">/</span><span style="color:#ADBAC7"> (volume </span><span style="color:#F47067">+</span><span style="color:#6CB6FF"> 1e-8</span><span style="color:#ADBAC7">)</span></span>
<span data-line=""><span style="color:#ADBAC7">importance </span><span style="color:#F47067">/=</span><span style="color:#ADBAC7"> importance.sum()</span></span>
<span data-line=""><span style="color:#ADBAC7">idx </span><span style="color:#F47067">=</span><span style="color:#ADBAC7"> rng.choice(</span><span style="color:#6CB6FF">len</span><span style="color:#ADBAC7">(x), </span><span style="color:#F69D50">size</span><span style="color:#F47067">=</span><span style="color:#6CB6FF">min</span><span style="color:#ADBAC7">(N, </span><span style="color:#6CB6FF">len</span><span style="color:#ADBAC7">(x)), </span><span style="color:#F69D50">replace</span><span style="color:#F47067">=</span><span style="color:#6CB6FF">False</span><span style="color:#ADBAC7">, </span><span style="color:#F69D50">p</span><span style="color:#F47067">=</span><span style="color:#ADBAC7">importance)</span></span></code></pre></figure>
<p>Then quantize positions to <code>uint16</code> and pull grayscale brightness from the DC
term of the spherical harmonics. Pack it all into a tight binary struct:</p>
<figure data-rehype-pretty-code-figure=""><pre style="background-color:#22272e;color:#adbac7" tabindex="0" data-language="python" data-theme="github-dark-dimmed"><code data-language="python" data-theme="github-dark-dimmed" style="display:grid"><span data-line=""><span style="color:#F47067">def</span><span style="color:#DCBDFB"> quant</span><span style="color:#ADBAC7">(a):</span></span>
<span data-line=""><span style="color:#ADBAC7">    lo, hi </span><span style="color:#F47067">=</span><span style="color:#ADBAC7"> a.min(), a.max()</span></span>
<span data-line=""><span style="color:#F47067">    return</span><span style="color:#ADBAC7"> lo, hi, np.round((a </span><span style="color:#F47067">-</span><span style="color:#ADBAC7"> lo) </span><span style="color:#F47067">/</span><span style="color:#ADBAC7"> (hi </span><span style="color:#F47067">-</span><span style="color:#ADBAC7"> lo) </span><span style="color:#F47067">*</span><span style="color:#6CB6FF"> 65535</span><span style="color:#ADBAC7">).astype(np.uint16)</span></span>
<span data-line=""> </span>
<span data-line=""><span style="color:#ADBAC7">dt </span><span style="color:#F47067">=</span><span style="color:#ADBAC7"> np.dtype([(</span><span style="color:#96D0FF">&quot;x&quot;</span><span style="color:#ADBAC7">, </span><span style="color:#96D0FF">&quot;&lt;u2&quot;</span><span style="color:#ADBAC7">), (</span><span style="color:#96D0FF">&quot;y&quot;</span><span style="color:#ADBAC7">, </span><span style="color:#96D0FF">&quot;&lt;u2&quot;</span><span style="color:#ADBAC7">), (</span><span style="color:#96D0FF">&quot;z&quot;</span><span style="color:#ADBAC7">, </span><span style="color:#96D0FF">&quot;&lt;u2&quot;</span><span style="color:#ADBAC7">), (</span><span style="color:#96D0FF">&quot;a&quot;</span><span style="color:#ADBAC7">, </span><span style="color:#96D0FF">&quot;u1&quot;</span><span style="color:#ADBAC7">), (</span><span style="color:#96D0FF">&quot;b&quot;</span><span style="color:#ADBAC7">, </span><span style="color:#96D0FF">&quot;u1&quot;</span><span style="color:#ADBAC7">)])</span></span></code></pre></figure>
<p>Five fields per point, no JSON, no headers.</p>
<p>To view it, I wrote a single TypeScript component, no dependencies. Each frame
it rotates the points around Y then X, depth-sorts them furthest-first, draws
each one as a 3×3 square into an <code>ImageData</code> buffer with alpha blending, and
flushes with <code>putImageData</code>.</p>
<p>The projection is just perspective division:</p>
<figure data-rehype-pretty-code-figure=""><pre style="background-color:#22272e;color:#adbac7" tabindex="0" data-language="typescript" data-theme="github-dark-dimmed"><code data-language="typescript" data-theme="github-dark-dimmed" style="display:grid"><span data-line=""><span style="color:#F47067">const</span><span style="color:#6CB6FF"> rx</span><span style="color:#F47067">  =</span><span style="color:#ADBAC7">  x[i] </span><span style="color:#F47067">*</span><span style="color:#ADBAC7"> cosY </span><span style="color:#F47067">+</span><span style="color:#ADBAC7"> z[i] </span><span style="color:#F47067">*</span><span style="color:#ADBAC7"> sinY;</span></span>
<span data-line=""><span style="color:#F47067">const</span><span style="color:#6CB6FF"> rz</span><span style="color:#F47067">  =</span><span style="color:#F47067"> -</span><span style="color:#ADBAC7">x[i] </span><span style="color:#F47067">*</span><span style="color:#ADBAC7"> sinY </span><span style="color:#F47067">+</span><span style="color:#ADBAC7"> z[i] </span><span style="color:#F47067">*</span><span style="color:#ADBAC7"> cosY;</span></span>
<span data-line=""><span style="color:#F47067">const</span><span style="color:#6CB6FF"> ry2</span><span style="color:#F47067"> =</span><span style="color:#ADBAC7">  y[i] </span><span style="color:#F47067">*</span><span style="color:#ADBAC7"> cosX </span><span style="color:#F47067">-</span><span style="color:#ADBAC7"> rz </span><span style="color:#F47067">*</span><span style="color:#ADBAC7"> sinX;</span></span>
<span data-line=""><span style="color:#F47067">const</span><span style="color:#6CB6FF"> rz2</span><span style="color:#F47067"> =</span><span style="color:#ADBAC7">  y[i] </span><span style="color:#F47067">*</span><span style="color:#ADBAC7"> sinX </span><span style="color:#F47067">+</span><span style="color:#ADBAC7"> rz </span><span style="color:#F47067">*</span><span style="color:#ADBAC7"> cosX;</span></span>
<span data-line=""><span style="color:#F47067">const</span><span style="color:#6CB6FF"> d</span><span style="color:#F47067">   =</span><span style="color:#ADBAC7"> rz2 </span><span style="color:#F47067">-</span><span style="color:#6CB6FF"> CAM_Z</span><span style="color:#ADBAC7">;</span></span>
<span data-line=""><span style="color:#F47067">const</span><span style="color:#6CB6FF"> sx</span><span style="color:#F47067">  =</span><span style="color:#ADBAC7"> (</span><span style="color:#6CB6FF">W</span><span style="color:#F47067"> *</span><span style="color:#6CB6FF"> 0.5</span><span style="color:#F47067"> +</span><span style="color:#ADBAC7"> (</span><span style="color:#6CB6FF">FOCAL</span><span style="color:#F47067"> *</span><span style="color:#ADBAC7"> rx)  </span><span style="color:#F47067">/</span><span style="color:#ADBAC7"> d </span><span style="color:#F47067">+</span><span style="color:#6CB6FF"> 0.5</span><span style="color:#ADBAC7">) </span><span style="color:#F47067">|</span><span style="color:#6CB6FF"> 0</span><span style="color:#ADBAC7">;</span></span>
<span data-line=""><span style="color:#F47067">const</span><span style="color:#6CB6FF"> sy</span><span style="color:#F47067">  =</span><span style="color:#ADBAC7"> (</span><span style="color:#6CB6FF">H</span><span style="color:#F47067"> *</span><span style="color:#6CB6FF"> 0.5</span><span style="color:#F47067"> +</span><span style="color:#ADBAC7"> (</span><span style="color:#6CB6FF">FOCAL</span><span style="color:#F47067"> *</span><span style="color:#ADBAC7"> ry2) </span><span style="color:#F47067">/</span><span style="color:#ADBAC7"> d </span><span style="color:#F47067">+</span><span style="color:#6CB6FF"> 0.5</span><span style="color:#ADBAC7">) </span><span style="color:#F47067">|</span><span style="color:#6CB6FF"> 0</span><span style="color:#ADBAC7">;</span></span></code></pre></figure>
<p>Mouse gives ±4° rotation. Touch drives a sinusoidal pan that coasts a little
after you lift your finger. An <code>IntersectionObserver</code> kills the animation loop
when the element isn&#x27;t on screen. The circular crop is <code>rounded-full overflow-hidden</code> on the wrapper.</p>
<p>15,000 points is genuinely not that many. I expected it to look sparse. It
doesn&#x27;t, the face reads clearly, rotation gives real depth, and it moves in a
way a flat photo can&#x27;t. Surprised at how well it held up.</p>]]></description>
            <link>https://eliseomartelli.it/blog/2026-03-28-gaussian-splat-portrait</link>
            <guid isPermaLink="true">https://eliseomartelli.it/blog/2026-03-28-gaussian-splat-portrait</guid>
            <category><![CDATA[Programming]]></category>
            <pubDate>Sat, 28 Mar 2026 12:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Darkroom: Silver Gelatin Print at Spazio Gnu]]></title>
            <description><![CDATA[<link rel="preload" as="image" href="/posts/2026-03-09-darkroom/L1001197.jpeg"/><i>Posted on: <span><a href="https://eliseomartelli.it/blog/tags/misc">Misc</a>, </span><span><a href="https://eliseomartelli.it/blog/tags/photography">Photography</a>.</span></i><p>Tagline: Where&#x27;s the undo button?</p><hr/><p><img src="/posts/2026-03-09-darkroom/L1001197.jpeg" alt="A picture of the darkroom"/></p>
<p>Yesterday I spent my afternoon at <a href="https://fotografiatorino.blogspot.com">Spazio Gnu</a>,
a shared photography space in Turin, Italy.<br/>
As someone who spends a lot of time working with digital tools (mostly
programming, photo editing, and video editing), stepping into a space
where there are no &quot;undo&quot; buttons or history states was a refreshing shift.</p>
<p>For this session, I used a DURST M700 enlarger, along with a box of FOMA
Fomaspeed Variant 311 High Gloss RC paper (8x10 inches), some rolls of
<a href="/blog/2026-01-06-developing-kodak-tri-x-400-at-1600-iso-in-bellini-euro-hc">home developed
film</a>,
a pack of Ilford Multigrade filters, and a few trays of chemicals.</p>
<p>I ended up walking away with a total of 8 prints, which I consider a solid outcome
for a first attempt at silver gelatin printing.</p>
<p>Spazio Gnu&#x27;s darkroom is organized into two (and an half) areas:</p>
<ul>
<li>A dry area, where you have the enlargers and the workbench to handle the
negatives and the paper.</li>
<li>A wet island at the center of the room where you have water and trays to
develop the prints.</li>
<li>A drying area with a roller dryer to speed up the drying process.</li>
</ul>
<p>The process of making a print requires a lot of patience and attention to
detail.</p>
<p>One of the first steps is to mix the chemicals.
Chemicals used in the darkroom are typically divided into three categories:
developer, stop bath, and fixer.</p>
<ul>
<li>Developer: This chemical makes the latent image appear on the paper. I used
ILFORD MULTIGRADE developer, mixed at a standard ratio of 1+9 with water.</li>
<li>Stop Bath: This abruptly brings the development to an end and prevents
contamination of the next bath. I used ILFORD ILFOSTOP at a 1+19 dilution.</li>
<li>Fixer: Makes the image permanent. ILFORD RAPID FIXER, mixed at 1+4.</li>
</ul>
<p>Before committing to a full sheet of paper, I made a test print to find the
correct exposure. By using my hand to cover part of the paper, I exposed the
sheet in 5-second increments (from 5 to 25 seconds), creating a gradient of
exposures on the same strip. Examining this test print allowed me to estimate
the correct exposure for the final image. I ended up choosing 15 seconds as the
optimal base exposure.</p>
<p>Because FOMA 311 is a variable contrast paper, I also had to select the right
filter to achieve the desired contrast. The ILFORD MULTIGRADE filters range
from 00 (very soft) to 5 (very hard). I predominantly used a grade 2 filter,
which produces a good range of print tones for an average negative.</p>
<p>On one of the prints, the subject was rendering a bit too dark, so I decided to
experiment with a technique called &quot;dodging.&quot; Dodging involves using your hands
or pieces of card to hold back light from selected areas of the print during
exposure. By physically blocking the enlarger&#x27;s light over my subject for a few
seconds, I was able to lighten them and bring out more detail in the shadows.</p>
<p>After exposing the paper, the real magic happens in the wet area.</p>
<p>I slid the paper into the developer tray. While the image begins to appear
almost immediately, the print needs a full 50 seconds in the developer to fully
mature. Then, using tongs, I transferred it to the stop bath and rocked the
dish for about 30 seconds. Finally, it went into the fixer to make the image
permanent. (While ILFORD notes fixing takes only 30 seconds in fresh solution,
I left mine in for about a minute to be safe).</p>
<p>After a quick wash in running water to remove any residual chemicals, I ran the
prints through the roller dryer.</p>
<p>Holding 8 physical photographs that you crafted entirely by hand is a highly
rewarding experience. Each print has its own unique character and subtle
imperfections, adding to its charm. Seeing an image slowly emerge in the
developer tray is a tactile experience that no digital workflow can replicate.</p>]]></description>
            <link>https://eliseomartelli.it/blog/2026-03-09-darkroom-silver-gelatin-print-at-spazio-gnu</link>
            <guid isPermaLink="true">https://eliseomartelli.it/blog/2026-03-09-darkroom-silver-gelatin-print-at-spazio-gnu</guid>
            <category><![CDATA[Misc]]></category>
            <category><![CDATA[Photography]]></category>
            <pubDate>Mon, 09 Mar 2026 09:54:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[The Bloat of Modern Fitness Apps]]></title>
            <description><![CDATA[<i>Posted on: <span><a href="https://eliseomartelli.it/blog/tags/misc">Misc</a>.</span></i><p>Tagline: Starting from 49.99/year, and your data.</p><hr/><p>Over the past week, I&#x27;ve been making an effort to strike a balance between my
digital and analog life. Between finally having the
<a href="/blog/2026-02-13-automating-my-cv-with-latex-and-github-actions">time</a> to do
some <a href="/blog/2026-02-17-less-but-better-a-braun-clock-for-the-r36s">personal
projects</a>, and my
usual photography walks, I started to rely on the gym as my
analog escape, thanks to my <a href="https://nomnp.com">friends</a>. I just want to put on
my headphones, listen to some Burial or JPEGMafia to tune out the background
noise, and lift.</p>
<p>The current state of digital fitness tools makes that surprisingly
difficult. Rather than acting as a simple tools that do one thing well, the
modern fitness app has become a bloated, privacy-invading ecosystem.</p>
<p>I tried out a few popular fitness apps, and the experience was underwhelming.</p>
<p>One of the biggest mistake gym apps make is demanding too much cognitive load
before you even log a workout. During onboarding, they want you to set up an
entire multi-day split, enter exact body stats, and sit through tutorials.
When using the app at the gym, resting for 60 seconds between sets, I need
a frictionless interface. Everything else just interrupts the physical flow.</p>
<p>Human physiology isn&#x27;t a simple math equation. When an app rigidly demands
that you maintain a streak or push through six consecutive training days
without rest, it ignores the realities of life and fatigue.</p>
<p>Fitness apps collect incredibly intimate data, from geolocation to biometric
vitals, yet many operate with broadly-worded privacy policies that allow them
to share this information with third-party data brokers and advertisers.</p>
<p>At the end of the day, a fitness app should be a good appliance:
it should capture the data perfectly, feel great to use, and then get
completely out of your way.</p>]]></description>
            <link>https://eliseomartelli.it/blog/2026-02-21-the-bloat-of-modern-fitness-apps</link>
            <guid isPermaLink="true">https://eliseomartelli.it/blog/2026-02-21-the-bloat-of-modern-fitness-apps</guid>
            <category><![CDATA[Misc]]></category>
            <pubDate>Sat, 21 Feb 2026 10:00:00 GMT</pubDate>
        </item>
    </channel>
</rss>