<?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: Abhishek Choudhary</title>
    <description>The latest articles on DEV Community by Abhishek Choudhary (@theabbie).</description>
    <link>https://dev.to/theabbie</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%2F315248%2F2bc4dc8d-1c1a-4593-ab75-e7e27f12aaf4.jpg</url>
      <title>DEV Community: Abhishek Choudhary</title>
      <link>https://dev.to/theabbie</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/theabbie"/>
    <language>en</language>
    <item>
      <title>Keeping Your Streamlit App Awake with Serverless Functions and a Free Cron Job</title>
      <dc:creator>Abhishek Choudhary</dc:creator>
      <pubDate>Thu, 22 Oct 2026 05:23:06 +0000</pubDate>
      <link>https://dev.to/theabbie/keeping-your-streamlit-app-awake-with-serverless-functions-and-a-free-cron-job-5eef</link>
      <guid>https://dev.to/theabbie/keeping-your-streamlit-app-awake-with-serverless-functions-and-a-free-cron-job-5eef</guid>
      <description>&lt;p&gt;You deploy a little Streamlit app on Community Cloud, share the link, and a day later it's showing a sleepy "Zzzz, this app has gone to sleep due to inactivity" page. Anyone who opens it has to click a button and wait for a cold start.&lt;/p&gt;

&lt;p&gt;There's no keep-alive switch in Streamlit Community Cloud, and the advice on the forums boils down to "push an empty commit" or "automate a browser". Both work, but both are heavier than they need to be. There's a simpler way: &lt;strong&gt;do exactly what that button does, with one HTTP request, on a schedule.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ This uses an undocumented endpoint that the Streamlit web page itself calls. It can change without notice. Only use it on apps you own, and keep the schedule gentle.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What the "wake up" button really does
&lt;/h2&gt;

&lt;p&gt;If you open dev tools on a sleeping app and click &lt;strong&gt;Yes, get this app back up!&lt;/strong&gt;, you'll see a single request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST https://&amp;lt;your-app&amp;gt;.streamlit.app/api/v2/app/resume
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It needs two small things besides the POST itself, and neither one is a login:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A CSRF token.&lt;/strong&gt; The page never stores it anywhere fancy. It simply remembers the &lt;code&gt;x-csrf-token&lt;/code&gt; &lt;em&gt;response header&lt;/em&gt; from the previous API call and sends it back. The server also sets a matching &lt;code&gt;_streamlit_csrf&lt;/code&gt; cookie on that same response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A machine id.&lt;/strong&gt; A random UUID sent as &lt;code&gt;x-streamlit-machine-id&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So the whole dance is: ask for the app status, grab the token and cookie, then POST to &lt;code&gt;resume&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it with curl
&lt;/h2&gt;

&lt;p&gt;I'm using &lt;code&gt;webpie.streamlit.app&lt;/code&gt; here, swap in your own app URL. Save this as &lt;code&gt;wake.sh&lt;/code&gt;:&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="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# Wake a sleeping Streamlit Community Cloud app.&lt;/span&gt;
&lt;span class="c"&gt;# Usage: ./wake.sh https://webpie.streamlit.app&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;APP_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;:?usage:&lt;span class="p"&gt; ./wake.sh https&lt;/span&gt;://webpie.streamlit.app&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;COOKIES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;mktemp&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;trap&lt;/span&gt; &lt;span class="s1"&gt;'rm -f "$COOKIES"'&lt;/span&gt; EXIT

&lt;span class="c"&gt;# Step 1: any API call hands us a CSRF cookie (saved in the jar)&lt;/span&gt;
&lt;span class="c"&gt;# and an x-csrf-token response header (we grab it here).&lt;/span&gt;
&lt;span class="nv"&gt;CSRF_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;
  curl &lt;span class="nt"&gt;--silent&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; /dev/null &lt;span class="nt"&gt;--dump-header&lt;/span&gt; - &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--cookie-jar&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$COOKIES&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$APP_URL&lt;/span&gt;&lt;span class="s2"&gt;/api/v2/app/status"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'\r'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;': '&lt;/span&gt; &lt;span class="s1"&gt;'tolower($1) == "x-csrf-token" { print $2 }'&lt;/span&gt;
&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Step 2: the browser also sends a random UUID as a machine id.&lt;/span&gt;
&lt;span class="nv"&gt;MACHINE_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;uuidgen | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="s1"&gt;'[:upper:]'&lt;/span&gt; &lt;span class="s1"&gt;'[:lower:]'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Step 3: this is the call behind the "Yes, get this app back up!" button.&lt;/span&gt;
curl &lt;span class="nt"&gt;--silent&lt;/span&gt; &lt;span class="nt"&gt;--include&lt;/span&gt; &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cookie&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$COOKIES&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--cookie-jar&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$COOKIES&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"x-csrf-token: &lt;/span&gt;&lt;span class="nv"&gt;$CSRF_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"x-streamlit-machine-id: &lt;/span&gt;&lt;span class="nv"&gt;$MACHINE_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"origin: &lt;/span&gt;&lt;span class="nv"&gt;$APP_URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"referer: &lt;/span&gt;&lt;span class="nv"&gt;$APP_URL&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$APP_URL&lt;/span&gt;&lt;span class="s2"&gt;/api/v2/app/resume"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt;

&lt;span class="c"&gt;# Step 4 (optional): check the status afterwards&lt;/span&gt;
curl &lt;span class="nt"&gt;--silent&lt;/span&gt; &lt;span class="nt"&gt;--cookie&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$COOKIES&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$APP_URL&lt;/span&gt;&lt;span class="s2"&gt;/api/v2/app/status"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&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;chmod&lt;/span&gt; +x wake.sh
./wake.sh https://webpie.streamlit.app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;204 No Content&lt;/code&gt; from the resume call means the request was accepted. The status call at the end shows the app's state; while it's booting, the &lt;code&gt;status&lt;/code&gt; number changes. (An app that's already awake just answers normally, so it's safe to run any time.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Make it run itself
&lt;/h2&gt;

&lt;p&gt;A shell script on your laptop doesn't help while you're asleep too. The nice part is that the logic is just &lt;code&gt;fetch&lt;/code&gt; calls, so it runs on almost any serverless platform: &lt;strong&gt;Cloudflare Workers, Vercel, Netlify, AWS Lambda, Deno Deploy&lt;/strong&gt;. Pick whichever you already use.&lt;/p&gt;

&lt;p&gt;Here's the core logic once, in plain JavaScript:&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;// Shared logic: wake a Streamlit Community Cloud app.&lt;/span&gt;
&lt;span class="c1"&gt;// Works in Cloudflare Workers, Vercel, Netlify, Node 18+, anything with fetch.&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;wakeApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;appUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Step 1: any API call gives us a CSRF cookie and an x-csrf-token header.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;status&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;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;appUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/v2/app/status`&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;csrfToken&lt;/span&gt; &lt;span class="o"&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;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-csrf-token&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;cookies&lt;/span&gt; &lt;span class="o"&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;headers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSetCookie&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="c1"&gt;// keep "name=value", drop attributes&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="c1"&gt;// Step 2: this is the call behind the "Yes, get this app back up!" button.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resume&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;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;appUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/v2/app/resume`&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="na"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-csrf-token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;csrfToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-streamlit-machine-id&lt;/span&gt;&lt;span class="dl"&gt;"&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="na"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;appUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;referer&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;appUrl&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;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resume&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Option A: Cloudflare Workers
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;worker.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;wakeApp&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="s2"&gt;./wake.js&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="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&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="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Optional: stop strangers from poking your endpoint&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&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;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WAKE_KEY&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt; &lt;span class="o"&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;WAKE_KEY&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Nope&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;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;401&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;result&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;wakeApp&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;APP_URL&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;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&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;Set &lt;code&gt;APP_URL&lt;/code&gt; (for example &lt;code&gt;https://webpie.streamlit.app&lt;/code&gt;) and an optional &lt;code&gt;WAKE_KEY&lt;/code&gt; as variables in the Worker settings, then deploy with &lt;code&gt;npx wrangler deploy&lt;/code&gt;. You'll get a URL like &lt;code&gt;https://wake-streamlit.&amp;lt;you&amp;gt;.workers.dev/?key=&amp;lt;your key&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option B: Vercel
&lt;/h3&gt;

&lt;p&gt;Put &lt;code&gt;wake.js&lt;/code&gt; in your project root and add &lt;code&gt;api/wake.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;wakeApp&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="s2"&gt;../wake.js&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="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;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;WAKE_KEY&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&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;WAKE_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Nope&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&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;wakeApp&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;APP_URL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&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;Add &lt;code&gt;APP_URL&lt;/code&gt; and &lt;code&gt;WAKE_KEY&lt;/code&gt; under Project Settings → Environment Variables and deploy. Your endpoint is &lt;code&gt;https://&amp;lt;project&amp;gt;.vercel.app/api/wake?key=&amp;lt;your key&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trigger it with cron-job.org (free)
&lt;/h2&gt;

&lt;p&gt;You don't even need the platform's own scheduler. &lt;a href="https://cron-job.org/en/" rel="noopener noreferrer"&gt;cron-job.org&lt;/a&gt; is free and will call any URL on a schedule:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an account and click &lt;strong&gt;Create cronjob&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;URL:&lt;/strong&gt; the Worker or Vercel endpoint from above, including &lt;code&gt;?key=...&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schedule:&lt;/strong&gt; every 6 hours is plenty. Apps seem to sleep after roughly 12 hours without visitors, so you want to land well inside that window.&lt;/li&gt;
&lt;li&gt;Save. Under the job's history you can see each run's status code. &lt;code&gt;200&lt;/code&gt; means your function ran.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's the whole setup: cron-job.org pokes your serverless function, the function pokes Streamlit, and your app stays warm. No browser, no Selenium, no fake commits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things worth knowing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It's unofficial.&lt;/strong&gt; If Streamlit changes the endpoint, the script breaks. Check the cron history now and then.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's polite use only.&lt;/strong&gt; One request every few hours for your own app. Please don't hammer it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Waking isn't the same as being busy.&lt;/strong&gt; This resumes a sleeping app. It doesn't pretend to be traffic, so the app can still go back to sleep later, which is why the schedule keeps repeating.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep the &lt;code&gt;key&lt;/code&gt; secret&lt;/strong&gt; so nobody else can trigger your function.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you'd rather stay inside the official lines, the alternative is hosting the app somewhere that doesn't sleep. But for a free-tier hobby app, a 20-line function and a free cron service is hard to beat.&lt;/p&gt;

&lt;p&gt;Happy hacking!&lt;/p&gt;

</description>
      <category>streamlit</category>
      <category>python</category>
      <category>serverless</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Building a Physics-Accurate Live Wallpaper for Android</title>
      <dc:creator>Abhishek Choudhary</dc:creator>
      <pubDate>Thu, 30 Jul 2026 09:28:54 +0000</pubDate>
      <link>https://dev.to/theabbie/building-a-physics-accurate-live-wallpaper-for-android-1gi9</link>
      <guid>https://dev.to/theabbie/building-a-physics-accurate-live-wallpaper-for-android-1gi9</guid>
      <description>&lt;p&gt;A while back I built a &lt;a href="https://github.com/theabbie/DoublePendulum" rel="noopener noreferrer"&gt;double-pendulum simulation in the browser&lt;/a&gt;. The motion is hard to stop watching. Two bobs, one hinge, and the trajectory never repeats. I kept thinking it would make a good live wallpaper, so I eventually ported it to Android.&lt;/p&gt;

&lt;p&gt;This is the story of how that port went.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting point: the web simulation
&lt;/h2&gt;

&lt;p&gt;The browser version runs entirely in a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element. Physics are integrated with RK4 over the Hamiltonian equations of motion, and the path tracer stores a circular buffer of past positions. The coordinate system is a normalised viewBox so it scales cleanly to any window size.&lt;/p&gt;

&lt;p&gt;The goal for the Android version was pixel-identical output: same stroke widths, same bob radii, same trace behavior, same graph. Not a visual approximation.&lt;/p&gt;

&lt;h2&gt;
  
  
  WallpaperService and the rendering surface
&lt;/h2&gt;

&lt;p&gt;Android live wallpapers subclass &lt;code&gt;WallpaperService&lt;/code&gt; and expose a &lt;code&gt;Surface&lt;/code&gt; through the &lt;code&gt;Engine&lt;/code&gt; callbacks. The canonical pattern is to acquire a &lt;code&gt;Canvas&lt;/code&gt; from the surface, draw into it, and post it back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;canvas&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;surfaceHolder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lockCanvas&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;surfaceHolder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unlockCanvasAndPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tricky part is the coordinate system. The surface dimensions are in physical pixels and vary across devices. To get pixel-identical output I mirrored the web approach exactly: define everything in a normalised viewBox of &lt;code&gt;100 x (100 * H/W)&lt;/code&gt; units, then scale the canvas once at the start of each frame:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;100f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;100f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stroke widths, bob radii, and the graph are all specified in viewBox units. The single scale transform handles the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Physics: Hamiltonian + RK4
&lt;/h2&gt;

&lt;p&gt;The double pendulum is not easily simulated with naive Euler integration because energy drifts quickly. The web version uses the Hamiltonian formulation, where generalised momenta are tracked alongside the angles, and RK4 advances the state each tick.&lt;/p&gt;

&lt;p&gt;Translating this from JavaScript to Kotlin was mostly mechanical. The main difference is that Kotlin does not have &lt;code&gt;let&lt;/code&gt; destructuring the same way, so the intermediate RK4 derivatives end up as explicit &lt;code&gt;DoubleArray&lt;/code&gt; copies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;rk4Step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;DoubleArray&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Double&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;DoubleArray&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;k1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;derivatives&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;k2&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;derivatives&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addScaled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dt&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;k3&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;derivatives&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addScaled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dt&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;k4&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;derivatives&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addScaled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;DoubleArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;dt&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;6.0&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&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;k2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&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;k3&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;k4&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&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 simulation runs at whatever frame rate the surface provides and scales the timestep accordingly, so speed on fast and slow devices stays consistent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping settings consistent across three contexts
&lt;/h2&gt;

&lt;p&gt;One of the more interesting design problems was state management. There are three distinct contexts that each need a different view of the settings:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;In-app preview&lt;/strong&gt; while the user is tweaking parameters&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The live wallpaper surface&lt;/strong&gt; running on the home screen&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Saved presets&lt;/strong&gt; the user wants to recall later&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If the settings activity wrote directly to the shared prefs that the wallpaper service reads, every slider drag would immediately change the live wallpaper. That felt wrong.&lt;/p&gt;

&lt;p&gt;The solution was three separate &lt;code&gt;SharedPreferences&lt;/code&gt; stores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;chaotic_session&lt;/code&gt;: written freely during the settings session, cleared on every app launch so it never bleeds into the wallpaper&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;chaotic_wallpaper&lt;/code&gt;: read by the &lt;code&gt;WallpaperService&lt;/code&gt;; written only when the user taps SET AS WALLPAPER&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;chaotic_persistent&lt;/code&gt;: stores named presets, survives the session&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This way the user can drag bobs, shuffle with RANDOM, adjust mass and speed, and watch the in-app preview update in real time. None of that touches the live wallpaper until they explicitly commit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Drag-to-customize while paused
&lt;/h2&gt;

&lt;p&gt;The in-app preview lets you drag the bobs to set the initial position. Hit detection maps the touch coordinates back through the inverse of the viewBox scale and checks distance against each bob's radius. When a bob is being dragged, the physics step is skipped and the state is overwritten with the new angles derived from the touch position.&lt;/p&gt;

&lt;p&gt;One edge case: if both bobs are near each other, you want the touch to grab the nearer one. A simple nearest-first sort on touch-distance handles it.&lt;/p&gt;

&lt;h2&gt;
  
  
  OLED and battery
&lt;/h2&gt;

&lt;p&gt;The wallpaper uses a pure black background (&lt;code&gt;0xFF000000&lt;/code&gt;), which means OLED pixels in the black regions are literally off. This is intentional.&lt;/p&gt;

&lt;p&gt;For battery, the &lt;code&gt;WallpaperService.Engine&lt;/code&gt; provides &lt;code&gt;onVisibilityChanged&lt;/code&gt;. When the wallpaper is not visible (another app is in the foreground, screen is off), the rendering loop stops:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onVisibilityChanged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Boolean&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;visible&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;startRendering&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;stopRendering&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 app also has zero permissions beyond &lt;code&gt;BIND_WALLPAPER&lt;/code&gt; and makes no network requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build setup
&lt;/h2&gt;

&lt;p&gt;The project uses AGP 8.7.3 with R8 minification and resource shrinking in release. Release signing goes through a gitignored &lt;code&gt;keystore.properties&lt;/code&gt; file; if the file is absent the build falls back to the debug key automatically so CI and forks can still build.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./gradlew :app:bundleRelease   &lt;span class="c"&gt;# AAB for Play Store&lt;/span&gt;
./gradlew :app:assembleRelease &lt;span class="c"&gt;# APK for sideloading&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;The final app is a faithful port of the web simulation, running as a live wallpaper. The physics are identical, the visuals are pixel-matched across screen sizes, and the settings model keeps the preview experience independent from the live wallpaper until you decide to commit.&lt;/p&gt;

&lt;p&gt;It is free, has no ads, and collects no data.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://play.google.com/store/apps/details?id=com.theabbie.chaos" rel="noopener noreferrer"&gt;Get it on Google Play&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/theabbie/ChaoticLiveWallpaper" rel="noopener noreferrer"&gt;Source on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>physics</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Canvas API</title>
      <dc:creator>Abhishek Choudhary</dc:creator>
      <pubDate>Thu, 21 Mar 2024 03:09:14 +0000</pubDate>
      <link>https://dev.to/theabbie/canvas-api-1db7</link>
      <guid>https://dev.to/theabbie/canvas-api-1db7</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for DEV Challenge v24.03.20, One Byte Explainer: Browser API or Feature.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Explainer
&lt;/h2&gt;

&lt;p&gt;The Canvas API provides a drawing surface for creating visually engaging graphics and images using JavaScript to dynamically render shapes, paths, text, and images, enabling the creation of interactive charts, animations, and games on the web.&lt;/p&gt;

</description>
      <category>frontendchallenge</category>
      <category>devchallenge</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Serialize and Deserialize BST</title>
      <dc:creator>Abhishek Choudhary</dc:creator>
      <pubDate>Sun, 19 Jun 2022 04:00:48 +0000</pubDate>
      <link>https://dev.to/theabbie/serialize-and-deserialize-bst-45dm</link>
      <guid>https://dev.to/theabbie/serialize-and-deserialize-bst-45dm</guid>
      <description>&lt;p&gt;Serialization is converting a data structure or object into a sequence of bits so that it can be stored in a file or memory buffer, or transmitted across a network connection link to be reconstructed later in the same or another computer environment.&lt;/p&gt;

&lt;p&gt;Design an algorithm to serialize and deserialize a &lt;strong&gt;binary search tree&lt;/strong&gt;. There is no restriction on how your serialization/deserialization algorithm should work. You need to ensure that a binary search tree can be serialized to a string, and this string can be deserialized to the original tree structure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The encoded string should be as compact as possible.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; root = [2,1,3]&lt;br&gt;
&lt;strong&gt;Output:&lt;/strong&gt; [2,1,3]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; root = []&lt;br&gt;
&lt;strong&gt;Output:&lt;/strong&gt; []&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  The number of nodes in the tree is in the range &lt;code&gt;[0, 104]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;0 &amp;lt;= Node.val &amp;lt;= 104&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  The input tree is &lt;strong&gt;guaranteed&lt;/strong&gt; to be a binary search tree.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;SOLUTION:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Codec:

    def serialize(self, root: Optional[TreeNode]) -&amp;gt; str:
        tree = {}
        paths = [(root, 0)]
        while len(paths) &amp;gt; 0:
            curr, i = paths.pop()
            if curr:
                tree[i] = curr.val
                paths.append((curr.left, 2 * i + 1))
                paths.append((curr.right, 2 * i + 2))
        return ";".join(["{}={}".format(k, v) for k, v in tree.items()])

    def deserialize(self, data: str) -&amp;gt; Optional[TreeNode]:
        vals = data.split(";")
        tree = {}
        for v in vals:
            if len(v) &amp;gt; 0:
                key, value = v.split("=")
                tree[int(key)] = int(value)
        if len(tree) == 0:
            return None
        root = TreeNode()
        paths = [(root, 0)]
        while len(paths) &amp;gt; 0:
            curr, i = paths.pop()
            curr.val = tree[i]
            if (2 * i + 1) in tree:
                curr.left = TreeNode()
                paths.append((curr.left, 2 * i + 1))
            if (2 * i + 2) in tree:
                curr.right = TreeNode()
                paths.append((curr.right, 2 * i + 2))
        return root

# Your Codec object will be instantiated and called as such:
# Your Codec object will be instantiated and called as such:
# ser = Codec()
# deser = Codec()
# tree = ser.serialize(root)
# ans = deser.deserialize(tree)
# return ans
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>leetcode</category>
      <category>dsa</category>
      <category>theabbie</category>
    </item>
    <item>
      <title>Valid Palindrome</title>
      <dc:creator>Abhishek Choudhary</dc:creator>
      <pubDate>Sun, 19 Jun 2022 03:30:36 +0000</pubDate>
      <link>https://dev.to/theabbie/valid-palindrome-5aoa</link>
      <guid>https://dev.to/theabbie/valid-palindrome-5aoa</guid>
      <description>&lt;p&gt;A phrase is a &lt;strong&gt;palindrome&lt;/strong&gt; if, after converting all uppercase letters into lowercase letters and removing all non-alphanumeric characters, it reads the same forward and backward. Alphanumeric characters include letters and numbers.&lt;/p&gt;

&lt;p&gt;Given a string &lt;code&gt;s&lt;/code&gt;, return &lt;code&gt;true&lt;/code&gt; &lt;em&gt;if it is a &lt;strong&gt;palindrome&lt;/strong&gt;, or&lt;/em&gt; &lt;code&gt;false&lt;/code&gt; &lt;em&gt;otherwise&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; s = "A man, a plan, a canal: Panama"&lt;br&gt;
&lt;strong&gt;Output:&lt;/strong&gt; true&lt;br&gt;
&lt;strong&gt;Explanation:&lt;/strong&gt; "amanaplanacanalpanama" is a palindrome.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; s = "race a car"&lt;br&gt;
&lt;strong&gt;Output:&lt;/strong&gt; false&lt;br&gt;
&lt;strong&gt;Explanation:&lt;/strong&gt; "raceacar" is not a palindrome.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 3:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; s = " "&lt;br&gt;
&lt;strong&gt;Output:&lt;/strong&gt; true&lt;br&gt;
&lt;strong&gt;Explanation:&lt;/strong&gt; s is an empty string "" after removing non-alphanumeric characters.&lt;br&gt;
Since an empty string reads the same forward and backward, it is a palindrome.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;1 &amp;lt;= s.length &amp;lt;= 2 * 105&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;s&lt;/code&gt; consists only of printable ASCII characters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;SOLUTION:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Solution:
    def isPalindrome(self, s: str) -&amp;gt; bool:
        s = "".join([c.lower() for c in s if c.isalnum()])
        return s == s[::-1]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>leetcode</category>
      <category>dsa</category>
      <category>theabbie</category>
    </item>
    <item>
      <title>Average Salary Excluding the Minimum and Maximum Salary</title>
      <dc:creator>Abhishek Choudhary</dc:creator>
      <pubDate>Sun, 19 Jun 2022 03:00:41 +0000</pubDate>
      <link>https://dev.to/theabbie/average-salary-excluding-the-minimum-and-maximum-salary-4gn6</link>
      <guid>https://dev.to/theabbie/average-salary-excluding-the-minimum-and-maximum-salary-4gn6</guid>
      <description>&lt;p&gt;You are given an array of &lt;strong&gt;unique&lt;/strong&gt; integers &lt;code&gt;salary&lt;/code&gt; where &lt;code&gt;salary[i]&lt;/code&gt; is the salary of the &lt;code&gt;ith&lt;/code&gt; employee.&lt;/p&gt;

&lt;p&gt;Return &lt;em&gt;the average salary of employees excluding the minimum and maximum salary&lt;/em&gt;. Answers within &lt;code&gt;10-5&lt;/code&gt; of the actual answer will be accepted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; salary = [4000,3000,1000,2000]&lt;br&gt;
&lt;strong&gt;Output:&lt;/strong&gt; 2500.00000&lt;br&gt;
&lt;strong&gt;Explanation:&lt;/strong&gt; Minimum salary and maximum salary are 1000 and 4000 respectively.&lt;br&gt;
Average salary excluding minimum and maximum salary is (2000+3000) / 2 = 2500&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; salary = [1000,2000,3000]&lt;br&gt;
&lt;strong&gt;Output:&lt;/strong&gt; 2000.00000&lt;br&gt;
&lt;strong&gt;Explanation:&lt;/strong&gt; Minimum salary and maximum salary are 1000 and 3000 respectively.&lt;br&gt;
Average salary excluding minimum and maximum salary is (2000) / 1 = 2000&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;3 &amp;lt;= salary.length &amp;lt;= 100&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;1000 &amp;lt;= salary[i] &amp;lt;= 106&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  All the integers of &lt;code&gt;salary&lt;/code&gt; are &lt;strong&gt;unique&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;SOLUTION:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Solution:
    def average(self, salary: List[int]) -&amp;gt; float:
        n = len(salary)
        total = 0
        currmin = float('inf')
        currmax = float('-inf')
        for i in range(n):
            currmin = min(currmin, salary[i])
            currmax = max(currmax, salary[i])
            total += salary[i]
        return (total - currmin - currmax) / (n - 2)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>leetcode</category>
      <category>dsa</category>
      <category>theabbie</category>
    </item>
    <item>
      <title>Backspace String Compare</title>
      <dc:creator>Abhishek Choudhary</dc:creator>
      <pubDate>Sun, 19 Jun 2022 02:30:40 +0000</pubDate>
      <link>https://dev.to/theabbie/backspace-string-compare-5c0c</link>
      <guid>https://dev.to/theabbie/backspace-string-compare-5c0c</guid>
      <description>&lt;p&gt;Given two strings &lt;code&gt;s&lt;/code&gt; and &lt;code&gt;t&lt;/code&gt;, return &lt;code&gt;true&lt;/code&gt; &lt;em&gt;if they are equal when both are typed into empty text editors&lt;/em&gt;. &lt;code&gt;'#'&lt;/code&gt; means a backspace character.&lt;/p&gt;

&lt;p&gt;Note that after backspacing an empty text, the text will continue empty.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; s = "ab#c", t = "ad#c"&lt;br&gt;
&lt;strong&gt;Output:&lt;/strong&gt; true&lt;br&gt;
&lt;strong&gt;Explanation:&lt;/strong&gt; Both s and t become "ac".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; s = "ab##", t = "c#d#"&lt;br&gt;
&lt;strong&gt;Output:&lt;/strong&gt; true&lt;br&gt;
&lt;strong&gt;Explanation:&lt;/strong&gt; Both s and t become "".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 3:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; s = "a#c", t = "b"&lt;br&gt;
&lt;strong&gt;Output:&lt;/strong&gt; false&lt;br&gt;
&lt;strong&gt;Explanation:&lt;/strong&gt; s becomes "c" while t becomes "b".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;1 &amp;lt;= s.length, t.length &amp;lt;= 200&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;s&lt;/code&gt; and &lt;code&gt;t&lt;/code&gt; only contain lowercase letters and &lt;code&gt;'#'&lt;/code&gt; characters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Follow up:&lt;/strong&gt; Can you solve it in &lt;code&gt;O(n)&lt;/code&gt; time and &lt;code&gt;O(1)&lt;/code&gt; space?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SOLUTION:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Solution:
    def backspaceCompare(self, s: str, t: str) -&amp;gt; bool:
        ss = ""
        tt = ""
        for c in s:
            if c == "#":
                ss = ss[:-1]
            else:
                ss += c
        for c in t:
            if c == "#":
                tt = tt[:-1]
            else:
                tt += c
        return ss == tt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>leetcode</category>
      <category>dsa</category>
      <category>theabbie</category>
    </item>
    <item>
      <title>Maximum Erasure Value</title>
      <dc:creator>Abhishek Choudhary</dc:creator>
      <pubDate>Sun, 19 Jun 2022 02:00:46 +0000</pubDate>
      <link>https://dev.to/theabbie/maximum-erasure-value-4m96</link>
      <guid>https://dev.to/theabbie/maximum-erasure-value-4m96</guid>
      <description>&lt;p&gt;You are given an array of positive integers &lt;code&gt;nums&lt;/code&gt; and want to erase a subarray containing&amp;nbsp;&lt;strong&gt;unique elements&lt;/strong&gt;. The &lt;strong&gt;score&lt;/strong&gt; you get by erasing the subarray is equal to the &lt;strong&gt;sum&lt;/strong&gt; of its elements.&lt;/p&gt;

&lt;p&gt;Return &lt;em&gt;the &lt;strong&gt;maximum score&lt;/strong&gt; you can get by erasing &lt;strong&gt;exactly one&lt;/strong&gt; subarray.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;An array &lt;code&gt;b&lt;/code&gt; is called to be a subarray of &lt;code&gt;a&lt;/code&gt; if it forms a contiguous subsequence of &lt;code&gt;a&lt;/code&gt;, that is, if it is equal to &lt;code&gt;a[l],a[l+1],...,a[r]&lt;/code&gt; for some &lt;code&gt;(l,r)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; nums = [4,2,4,5,6]&lt;br&gt;
&lt;strong&gt;Output:&lt;/strong&gt; 17&lt;br&gt;
&lt;strong&gt;Explanation:&lt;/strong&gt; The optimal subarray here is [2,4,5,6].&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; nums = [5,2,1,2,5,2,1,2,5]&lt;br&gt;
&lt;strong&gt;Output:&lt;/strong&gt; 8&lt;br&gt;
&lt;strong&gt;Explanation:&lt;/strong&gt; The optimal subarray here is [5,2,1] or [1,2,5].&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;1 &amp;lt;= nums.length &amp;lt;= 105&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;1 &amp;lt;= nums[i] &amp;lt;= 104&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;SOLUTION:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Solution:
    def maximumUniqueSubarray(self, nums: List[int]) -&amp;gt; int:
        n = len(nums)
        msum = 0
        p = [0]
        for el in nums:
            p.append(p[-1] + el)
        prev = {}
        i = 0
        for j in range(n):
            if nums[j] in prev:
                i = max(i, prev[nums[j]] + 1)
            msum = max(msum, p[j + 1] - p[i])
            prev[nums[j]] = j
        return msum
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>leetcode</category>
      <category>dsa</category>
      <category>theabbie</category>
    </item>
    <item>
      <title>Balance a Binary Search Tree</title>
      <dc:creator>Abhishek Choudhary</dc:creator>
      <pubDate>Sun, 19 Jun 2022 01:30:42 +0000</pubDate>
      <link>https://dev.to/theabbie/balance-a-binary-search-tree-29i1</link>
      <guid>https://dev.to/theabbie/balance-a-binary-search-tree-29i1</guid>
      <description>&lt;p&gt;Given the &lt;code&gt;root&lt;/code&gt; of a binary search tree, return &lt;em&gt;a &lt;strong&gt;balanced&lt;/strong&gt; binary search tree with the same node values&lt;/em&gt;. If there is more than one answer, return &lt;strong&gt;any of them&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A binary search tree is &lt;strong&gt;balanced&lt;/strong&gt; if the depth of the two subtrees of every node never differs by more than &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c_ViwiMq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.leetcode.com/uploads/2021/08/10/balance1-tree.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c_ViwiMq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.leetcode.com/uploads/2021/08/10/balance1-tree.jpg" alt="" width="714" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; root = [1,null,2,null,3,null,4,null,null]&lt;br&gt;
&lt;strong&gt;Output:&lt;/strong&gt; [2,1,3,null,null,null,4]&lt;br&gt;
&lt;strong&gt;Explanation:&lt;/strong&gt; This is not the only correct answer, [3,1,4,null,2] is also correct.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T-zN1ACJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.leetcode.com/uploads/2021/08/10/balanced2-tree.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T-zN1ACJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.leetcode.com/uploads/2021/08/10/balanced2-tree.jpg" alt="" width="224" height="145"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; root = [2,1,3]&lt;br&gt;
&lt;strong&gt;Output:&lt;/strong&gt; [2,1,3]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  The number of nodes in the tree is in the range &lt;code&gt;[1, 104]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;1 &amp;lt;= Node.val &amp;lt;= 105&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;SOLUTION:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def inorder(self, root):
        if root:
            self.inorder(root.left)
            self.tree.append(root.val)
            self.inorder(root.right)

    def arrToTree(self, arr, i, j):
        if i &amp;lt;= j - 1:
            root = TreeNode()
            mid = (i + j) // 2
            root.val = arr[mid]
            root.left = self.arrToTree(arr, i, mid)
            root.right = self.arrToTree(arr, mid + 1, j)
            return root
        return None

    def balanceBST(self, root: TreeNode) -&amp;gt; TreeNode:
        self.tree = []
        self.inorder(root)
        return self.arrToTree(self.tree, 0, len(self.tree))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>leetcode</category>
      <category>dsa</category>
      <category>theabbie</category>
    </item>
    <item>
      <title>Smallest String With Swaps</title>
      <dc:creator>Abhishek Choudhary</dc:creator>
      <pubDate>Sun, 19 Jun 2022 01:00:46 +0000</pubDate>
      <link>https://dev.to/theabbie/smallest-string-with-swaps-2nof</link>
      <guid>https://dev.to/theabbie/smallest-string-with-swaps-2nof</guid>
      <description>&lt;p&gt;You are given a string &lt;code&gt;s&lt;/code&gt;, and an array of pairs of indices in the string&amp;nbsp;&lt;code&gt;pairs&lt;/code&gt;&amp;nbsp;where&amp;nbsp;&lt;code&gt;pairs[i] =&amp;nbsp;[a, b]&lt;/code&gt;&amp;nbsp;indicates 2 indices(0-indexed) of the string.&lt;/p&gt;

&lt;p&gt;You can&amp;nbsp;swap the characters at any pair of indices in the given&amp;nbsp;&lt;code&gt;pairs&lt;/code&gt;&amp;nbsp;&lt;strong&gt;any number of times&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Return the&amp;nbsp;lexicographically smallest string that &lt;code&gt;s&lt;/code&gt;&amp;nbsp;can be changed to after using the swaps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; s = "dcab", pairs = [[0,3],[1,2]]&lt;br&gt;
&lt;strong&gt;Output:&lt;/strong&gt; "bacd"&lt;br&gt;
&lt;strong&gt;Explaination:&lt;/strong&gt; &lt;br&gt;
Swap s[0] and s[3], s = "bcad"&lt;br&gt;
Swap s[1] and s[2], s = "bacd"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; s = "dcab", pairs = [[0,3],[1,2],[0,2]]&lt;br&gt;
&lt;strong&gt;Output:&lt;/strong&gt; "abcd"&lt;br&gt;
&lt;strong&gt;Explaination:&lt;/strong&gt; &lt;br&gt;
Swap s[0] and s[3], s = "bcad"&lt;br&gt;
Swap s[0] and s[2], s = "acbd"&lt;br&gt;
Swap s[1] and s[2], s = "abcd"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 3:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; s = "cba", pairs = [[0,1],[1,2]]&lt;br&gt;
&lt;strong&gt;Output:&lt;/strong&gt; "abc"&lt;br&gt;
&lt;strong&gt;Explaination:&lt;/strong&gt; &lt;br&gt;
Swap s[0] and s[1], s = "bca"&lt;br&gt;
Swap s[1] and s[2], s = "bac"&lt;br&gt;
Swap s[0] and s[1], s = "abc"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;1 &amp;lt;= s.length &amp;lt;= 10^5&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;0 &amp;lt;= pairs.length &amp;lt;= 10^5&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;0 &amp;lt;= pairs[i][0], pairs[i][1] &amp;lt;&amp;nbsp;s.length&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;s&lt;/code&gt;&amp;nbsp;only contains lower case English letters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;SOLUTION:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from collections import defaultdict

class Solution:
    def dfs(self, graph, node, visited):
        for j in graph[node]:
            if j not in visited:
                visited.add(j)
                self.dfs(graph, j, visited)

    def smallestStringWithSwaps(self, s: str, pairs: List[List[int]]) -&amp;gt; str:
        op = list(s)
        graph = defaultdict(list)
        for a, b in pairs:
            graph[a].append(b)
            graph[b].append(a)
        globalvisited = set()
        for x in graph:
            if x not in globalvisited:
                currvisited = {x}
                self.dfs(graph, x, currvisited)
                indexes = sorted(currvisited)
                indexes_asc = sorted(currvisited, key = lambda p: s[p])
                n = len(indexes)
                for i in range(n):
                    op[indexes[i]] = s[indexes_asc[i]]
                globalvisited.update(currvisited)
        return "".join(op)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>leetcode</category>
      <category>dsa</category>
      <category>theabbie</category>
    </item>
    <item>
      <title>Crawler Log Folder</title>
      <dc:creator>Abhishek Choudhary</dc:creator>
      <pubDate>Sun, 19 Jun 2022 00:30:36 +0000</pubDate>
      <link>https://dev.to/theabbie/crawler-log-folder-6d5</link>
      <guid>https://dev.to/theabbie/crawler-log-folder-6d5</guid>
      <description>&lt;p&gt;The Leetcode file system keeps a log each time some user performs a &lt;em&gt;change folder&lt;/em&gt; operation.&lt;/p&gt;

&lt;p&gt;The operations are described below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;"../"&lt;/code&gt; : Move to the parent folder of the current folder. (If you are already in the main folder, &lt;strong&gt;remain in the same folder&lt;/strong&gt;).&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;"./"&lt;/code&gt; : Remain in the same folder.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;"x/"&lt;/code&gt; : Move to the child folder named &lt;code&gt;x&lt;/code&gt; (This folder is &lt;strong&gt;guaranteed to always exist&lt;/strong&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You are given a list of strings &lt;code&gt;logs&lt;/code&gt; where &lt;code&gt;logs[i]&lt;/code&gt; is the operation performed by the user at the &lt;code&gt;ith&lt;/code&gt; step.&lt;/p&gt;

&lt;p&gt;The file system starts in the main folder, then the operations in &lt;code&gt;logs&lt;/code&gt; are performed.&lt;/p&gt;

&lt;p&gt;Return &lt;em&gt;the minimum number of operations needed to go back to the main folder after the change folder operations.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iK3QcfMn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.leetcode.com/uploads/2020/09/09/sample_11_1957.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iK3QcfMn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.leetcode.com/uploads/2020/09/09/sample_11_1957.png" alt="" width="800" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; logs = ["d1/","d2/","../","d21/","./"]&lt;br&gt;
&lt;strong&gt;Output:&lt;/strong&gt; 2&lt;br&gt;
&lt;strong&gt;Explanation:&lt;/strong&gt; Use this change folder operation "../" 2 times and go back to the main folder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4SOJo-fr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.leetcode.com/uploads/2020/09/09/sample_22_1957.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4SOJo-fr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.leetcode.com/uploads/2020/09/09/sample_22_1957.png" alt="" width="800" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; logs = ["d1/","d2/","./","d3/","../","d31/"]&lt;br&gt;
&lt;strong&gt;Output:&lt;/strong&gt; 3&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 3:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; logs = ["d1/","../","../","../"]&lt;br&gt;
&lt;strong&gt;Output:&lt;/strong&gt; 0&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;1 &amp;lt;= logs.length &amp;lt;= 103&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;2 &amp;lt;= logs[i].length &amp;lt;= 10&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;logs[i]&lt;/code&gt; contains lowercase English letters, digits, &lt;code&gt;'.'&lt;/code&gt;, and &lt;code&gt;'/'&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;logs[i]&lt;/code&gt; follows the format described in the statement.&lt;/li&gt;
&lt;li&gt;  Folder names consist of lowercase English letters and digits.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;SOLUTION:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Solution:
    def minOperations(self, logs: List[str]) -&amp;gt; int:
        ctr = 0
        for log in logs:
            if log == "../":
                ctr =  max(ctr - 1, 0)
            elif log != "./":
                ctr += 1
        return ctr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>leetcode</category>
      <category>dsa</category>
      <category>theabbie</category>
    </item>
    <item>
      <title>Remove Palindromic Subsequences</title>
      <dc:creator>Abhishek Choudhary</dc:creator>
      <pubDate>Sun, 19 Jun 2022 00:00:45 +0000</pubDate>
      <link>https://dev.to/theabbie/remove-palindromic-subsequences-2hmj</link>
      <guid>https://dev.to/theabbie/remove-palindromic-subsequences-2hmj</guid>
      <description>&lt;p&gt;You are given a string &lt;code&gt;s&lt;/code&gt; consisting &lt;strong&gt;only&lt;/strong&gt; of letters &lt;code&gt;'a'&lt;/code&gt; and &lt;code&gt;'b'&lt;/code&gt;. In a single step you can remove one &lt;strong&gt;palindromic subsequence&lt;/strong&gt; from &lt;code&gt;s&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Return &lt;em&gt;the &lt;strong&gt;minimum&lt;/strong&gt; number of steps to make the given string empty&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;A string is a &lt;strong&gt;subsequence&lt;/strong&gt; of a given string if it is generated by deleting some characters of a given string without changing its order. Note that a subsequence does &lt;strong&gt;not&lt;/strong&gt; necessarily need to be contiguous.&lt;/p&gt;

&lt;p&gt;A string is called &lt;strong&gt;palindrome&lt;/strong&gt; if is one that reads the same backward as well as forward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; s = "ababa"&lt;br&gt;
&lt;strong&gt;Output:&lt;/strong&gt; 1&lt;br&gt;
&lt;strong&gt;Explanation:&lt;/strong&gt; s is already a palindrome, so its entirety can be removed in a single step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; s = "abb"&lt;br&gt;
&lt;strong&gt;Output:&lt;/strong&gt; 2&lt;br&gt;
&lt;strong&gt;Explanation:&lt;/strong&gt; "abb" -&amp;gt; "bb" -&amp;gt; "". &lt;br&gt;
Remove palindromic subsequence "a" then "bb".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 3:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; s = "baabb"&lt;br&gt;
&lt;strong&gt;Output:&lt;/strong&gt; 2&lt;br&gt;
&lt;strong&gt;Explanation:&lt;/strong&gt; "baabb" -&amp;gt; "b" -&amp;gt; "". &lt;br&gt;
Remove palindromic subsequence "baab" then "b".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;1 &amp;lt;= s.length &amp;lt;= 1000&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;s[i]&lt;/code&gt; is either &lt;code&gt;'a'&lt;/code&gt; or &lt;code&gt;'b'&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;SOLUTION:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Solution:
    def removePalindromeSub(self, s: str) -&amp;gt; int:
        if s == s[::-1]:
            return 1
        return 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>leetcode</category>
      <category>dsa</category>
      <category>theabbie</category>
    </item>
  </channel>
</rss>
