Skip to main content

On being an Obsidian power user

·1114 words·6 mins
Cool Tools

My boss recently called me an “Obsidian power user” and - how flattering! So let us take a tour of this, ahem, POWER.

What be Obsidian?
#

Obsidian is a closed source note-taking app. It has cultural roots and overlap with the Zettelkasten knowledge management system/lifestyle/philosophy, and has lots of overlap with Evernote - which is what I used for many years, before it became progressively enshittified.

What I like about Obsidian, and what keeps it very sticky for me, is:

  • Notes are kept in Markdown (.md) files. Soooo…
  • You can use Markdown syntax: especially code snippets,
code blocks

and LaTeX-ish math. You can do all your usual Markdown/HTML things, like linking across notes, across the web, embedding images and… uh, that’s it, I think?

But honestly: nice formatting of code, writing, and math = good enough for me!

Obsidian and the Zettelkasten people also tout its pretty graph representation of knowledge, e.g. my knowledge as a ball:

A screenshot of an Obsidian graph view

Tbh I never found much use of the ball o’ thoughts view.

How I use Obsidian
#

Every morning, when I open the Obsidian app on my personal or work laptop, my daily note springs up - auto-populated with its daily template (core plugin: daily notes). The title of this note is always a date, string-formatted to YYYY-MM-DD (day of week).md. I also enabled a nifty auto-conversion of natural language dates, so that if I type “@Yesterday”, it automatically converts it to “2025-07-02 (Wednesday)” which also becomes a clickable link to that day’s note 🀯 (community plugin: nldates).

Anyway, this daily note template includes tons of emoji (community plugin: emoji shortcodes), because I am a super-basic visual person and emoji are the new hieroglyphics and they give me joy. The template includes three main sections:

  1. βœ… TODOs
    1. πŸ” Daily rituals
    2. 🎯 Today’s specific TODOs
  2. πŸ““ NOTES
  3. 🧠 Some meta-analysis about what I’m working on

All the βœ…TODOs are tasks (community plugin: tasks) which I format to allow for partial completion and not-doings (theme: things). I give each task a time estimate (e.g. “30m”) and flag it as #work or #personal, as well as a due date (if applicable). As you can see, this is a bit of a Frankenstein mish-mash of the plugins’ capabilities and what I like.

For πŸ” daily rituals, these are the things I do to “clear my desk” every day - checking work notifications, zero inbox, Anki, and a reminder to frickin’ MOVE (aka some kind of exercise, eating vegetables, etc).

For 🎯 today’s TODOs, I used to just append an ever-increasing number of things here, but lately I’ve switched:

  • I keep a BACKLOG.md note, where I dump all the stuff I will eventually need/want to do.
  • I pull 2-3 TODOs into my daily note for today. Keeping this short and focused makes it way more likely I actually achieve something by the end of the day.

For πŸ““ NOTES, this is where I just brain dump all my thoughts about my tasks - as I work through them. It’s where I scribble pseudo-code snippets, draft docs (including blog posts!), meeting notes, TILs and notes to self, etc. This can be short or long.

And now - the piΓ¨ce de rΓ©sistance! - I discovered recently that, since Obsidian is basically a webpage (HTML, Markdown, JavaScript, CSS) - you can… CODE-IFY IT?!! aka I discovered that you can write short JavaScript-esque snippets to analyze your notes (community plugin: dataviewjs). So I have a little code block in my daily template, co-written by Claude, that parses my notes and my BACKLOG.md to give an estimate of how much time I’m planning to work (this is a good sanity check!) and if there’s anything urgent bubbling up from the backlog. Here it is:

function getTodayFileName() {
    const date = new Date();
    const formatter = new Intl.DateTimeFormat('en', {
        year: 'numeric',
        month: 'short',
        day: '2-digit',
        weekday: 'long'
    });
    
    const parts = formatter.formatToParts(date);
    const values = parts.reduce((acc, part) => {
        acc[part.type] = part.value;
        return acc;
    }, {});
    
    return `discord/${values.year}-${values.month}-${values.day} (${values.weekday}).md`;
}

// Get today's tasks
const todayFile = getTodayFileName();
let todaysTasks = dv.page(todayFile).file.tasks;
console.log("Found tasks:", todaysTasks.length);

// Get waiting tasks from backlog
const backlogFile = 'discord/BACKLOG.md'
const waitingTasks = dv.page(backlogFile).file.tasks.filter(t => t.text.includes('#waiting'));
console.log("Found waiting tasks:", waitingTasks.length);

// Get tasks that are due soon or past due
// Create a date 7 days in the future
const today = new Date();
const sevenDaysLater = new Date(); 
sevenDaysLater.setDate(today.getDate() + 7);

// Filter for tasks with due dates in next 7 days
// Assuming due dates are in format πŸ“… YYYY-MM-DD
const dueSoonTasks = dv.page(backlogFile).file.tasks.filter(t => {
    const dueDateMatch = t.text.match(/πŸ“…\s*(\d{4}-\d{2}-\d{2})/);
    if (!dueDateMatch) return false;
    
    const dueDate = new Date(dueDateMatch[1]);
    return dueDate <= sevenDaysLater && !t.completed;
});
console.log("Found due soon tasks:", dueSoonTasks.length);

// Parse all tasks
function parseTask(t) {
    const timeMatch = t.text.match(/(\d+)([mh])/);
    const timeEst = timeMatch ? parseInt(timeMatch[1]) : null;
    const timeUnit = timeMatch ? timeMatch[2] : null;
    const mins = timeUnit === 'h' ? timeEst * 60 : timeEst;
    
    const dueDateMatch = t.text.match(/πŸ“…\s*(\d{4}-\d{2}-\d{2})/);
    const dueDate = dueDateMatch ? new Date(dueDateMatch[1]) : null;
    
    const isWaiting = t.text.includes('#waiting');
    
    return {
        text: t.text.replace(/⏲ \d+[mh]/, '').replace(/πŸ“…\s*\d{4}-\d{2}-\d{2}/, '').trim(),
        mins: mins,
        dueDate: dueDate,
        isWaiting: isWaiting,
        completed: t.completed,
        willNotDo: t.status === '-'
    };
}

let parsedTodaysTasks = [...todaysTasks].map(t => parseTask(t));
let parsedWaitingTasks = [...waitingTasks].map(t => parseTask(t));
let parsedDueSoonTasks = [...dueSoonTasks].map(t => parseTask(t));

// Calculate total time (excluding waiting tasks)
const activeTasksOnly = parsedTodaysTasks.filter(t => !t.isWaiting && !t.completed && !t.willNotDo);
const totalMins = activeTasksOnly.reduce((sum, t) => sum + (t.mins || 0), 0);

dv.header(1, `🧠 Today's workload: ${Math.floor(totalMins/60)}h ${totalMins%60}m`);

// Show tasks due soon
dv.header(2, "πŸ“… Due Soon");
dv.table(
    ["Task", "Due Date", "Time Est."],
    parsedDueSoonTasks
        .filter(t => t.dueDate && !t.completed && !t.willNotDo)
        .sort((a, b) => a.dueDate - b.dueDate)
        .map(t => [
            t.text, 
            t.dueDate ? t.dueDate.toISOString().split('T')[0] : "", 
            t.mins ? `${Math.floor(t.mins/60)}h ${t.mins%60}m` : ""
        ])
);

// Show waiting tasks
dv.header(2, "⏳ Waiting On");
dv.list(
    parsedWaitingTasks
        .filter(t => t.isWaiting && !t.completed)
        .map(t => t.text)
);

Overall, I use syncthing to keep my notes synched between work and personal laptops, as well as my phone.

The once and future king
#

I’ve been using Obsidian now for a few years. It’s very sticky. I like it a lot. The one thing that I miss from my Macbook days is a good time tracker - e.g. I really miss Qbserve, which served me well for many years indeed. I’ve tried ActivityWatch, a Linux FOSS alternative, but it didn’t Just Work β„’ in the usual way and I eventually got fed up with it. I might give it another shot, but… well, I do miss seeing my actual time worked (rather than estimated time worked).

Another nice-to-have might be integrated Pomodoro? Though I’ve been pretty happy with using my physical hourglass (!).

Related

Reading things later with Wallabag and my Kobo
·675 words·4 mins
Tutorial Cool Tools Self Hosting
Pocket is dead, long live Wallabag
Moving everything into RSS
·974 words·5 mins
Tutorial Cool Tools
I heard you like websites so I put websites in your website so you could
This blog is now brought to you by Hugo
·630 words·3 mins
Cool Tools Fun Meta
Welp! It was time!