anurag
  • 2025
  • film
  • about
  • contact
blogs

countdown widget

Date
July 1, 2025
Tags

so I really like working towards a goal. it really helps me stay motivated by having a visual countdown to keep me on track.

that’s why I built this simple countdown widget for my iPhone. it shows how many days are left until my goal-currently the JLPT N3 exam on December 7 2025.

having this on my home screen keeps me constantly aware of how much time I have left, so I’m less likely to slack off.

here’s how it looks on my home screen:

image

Here’s how you can set it up as well:

requirements

  • any iphone
    • scriptable is only available for ios currently
  • scriptable
    • this is where you’ll paste the code and it’ll display on your home screen

steps

  1. install the above app on your iphone
  2. open the app and click the plus(+) button on the top right to create a new script, name the script whatever you’d like
  3. paste the below code in the editor and run it.
  1. edit these two lines at the top:
const title = "JLPT N3";
const targetDate = new Date("2025-12-07");

change them to whateever you’re counting down to.

  1. on lines 14-15, you’ll see:
const widgetWidth = 600;
const widgetHeight = 300;

these values are customized for ‘my iphone 14 plus’ - they might not look on other iphones. ask chatgpt to adjust it for your model.

  1. hit run.
  2. go to your iphone’s ‘home screen’, long press to enter ‘edit mode’, tap +, and search for ‘scriptable’.
  3. add a ‘medium-sized widget’, place it where you want.
  4. tap and hold the widget, then select ‘edit widget’, and you’ll see the screen below
  5. image
  1. under ‘script’, choose the one you just created and under ‘when interacting’, choose ‘run script’ and you’re done!
anurag

📍currently in san diego

const title = "JLPT N3";
const targetDate = new Date("2025-12-07");

const now = new Date();
const totalDays = 365;
const cols = 30;
const rows = Math.ceil(totalDays / cols);
const daysLeft = Math.max(0, Math.ceil((targetDate - now) / (1000 * 60 * 60 * 24)));
const daysPassed = Math.min(totalDays, totalDays - daysLeft);

let widget = new ListWidget();
widget.backgroundColor = new Color("#9FB1AF");

const widgetWidth = 600;
const widgetHeight = 300;
const canvasWidth = widgetWidth * 1.1;
const canvasHeight = widgetHeight * 0.75;

const dotSpacingX = canvasWidth / cols;
const dotSpacingY = canvasHeight / rows;
const dotDiameter = Math.min(dotSpacingX, dotSpacingY) * 0.7;

const draw = new DrawContext();
draw.size = new Size(canvasWidth, canvasHeight);
draw.opaque = false;

for (let i = 0; i < totalDays; i++) {
  const col = i % cols;
  const row = Math.floor(i / cols);
  const centerX = col * dotSpacingX + dotSpacingX / 2;
  const centerY = row * dotSpacingY + dotSpacingY / 2;
  const color = i < daysPassed ? Color.white() : new Color("#C1C9C8");
  draw.setFillColor(color);
  draw.fillEllipse(new Rect(
    centerX - dotDiameter / 2,
    centerY - dotDiameter / 2,
    dotDiameter,
    dotDiameter
  ));
}

const img = draw.getImage();
const imageStack = widget.addStack();
imageStack.layoutHorizontally();
let imgView = imageStack.addImage(img);
imgView.resizable = true;
imgView.widthWeight = 1.0;

widget.addSpacer(8);

let footer = widget.addStack();
footer.layoutHorizontally();

let leftText = footer.addText(title);
leftText.font = Font.semiboldSystemFont(14);
leftText.textColor = Color.white();

footer.addSpacer();

let rightText = footer.addText(daysLeft + " days left");
rightText.font = Font.systemFont(14);
rightText.textColor = Color.white();

if (config.runsInWidget) {
  Script.setWidget(widget);
} else {
  widget.presentMedium();
}

Script.complete();