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:

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:
- β
TODOs
- π Daily rituals
- π― Today’s specific TODOs
- π NOTES
- π§ 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.mdnote, 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 (!).