Benjamin Patterson658 downloadsAdds formula support to markdown tables. Use =SUM(A1:A5), =AVG(B1:B3), =A1*B1, etc. in any table cell.
Add spreadsheet-style formulas to your markdown tables. Write =SUM(A1:A5), =B1*C1, or =AVERAGE(A1:A3) directly in any table cell and see the results rendered inline — in both Live Preview and Reading view.
Add {{calc}} on the line immediately above your table to opt it into formula evaluation:
{{calc}}
| Item | Cost | Qty | Total |
|-------------|------|-----|-------------|
| LLC filing | 200 | 1 | =B1*C1 |
| Dev account | 99 | 1 | =B2*C2 |
| Accountant | 250 | 1 | =B3*C3 |
| **Total** | | | =SUM(D1:D3) |
The plugin hides the {{calc}} marker and adds column letters (A, B, C…) above the header and row numbers on the left so you always know which cell to reference.
Tables without a {{calc}} marker are left completely untouched.
| Label | Meaning |
|---|---|
A1 |
Row 1, first column |
B2 |
Row 2, second column |
A1:A5 |
Range: column A, rows 1 through 5 |
A1:C3 |
2D range: columns A–C, rows 1–3 |
| Formula | Description | Example |
|---|---|---|
=SUM(A1:A5) |
Sum a range | =SUM(D1:D3) |
=AVERAGE(A1:A5) |
Average of a range | =AVERAGE(B1:B4) |
=MEDIAN(A1:A5) |
Median of a range | =MEDIAN(B1:B5) |
=MIN(A1:A5) |
Smallest value | =MIN(B1:B10) |
=MAX(A1:A5) |
Largest value | =MAX(B1:B10) |
=COUNT(A1:A5) |
Count numeric cells | =COUNT(A1:A5) |
=COUNTA(A1:A5) |
Count non-empty cells (numbers or text) | =COUNTA(A1:A5) |
=PRODUCT(A1:A5) |
Multiply a range together | =PRODUCT(B1:B3) |
=STDEV(A1:A5) |
Sample standard deviation | =STDEV(B1:B10) |
=VAR(A1:A5) |
Sample variance | =VAR(B1:B10) |
=ABS(A1) |
Absolute value | =ABS(A3) |
=ROUND(A1, 2) |
Round to N decimals | =ROUND(B1, 2) |
=FLOOR(A1) |
Round down to nearest integer | =FLOOR(B1) |
=CEILING(A1) |
Round up to nearest integer | =CEILING(B1) |
=TRUNC(A1, 2) |
Truncate to N decimals (no rounding) | =TRUNC(B1, 2) |
=INT(A1) |
Round down to integer | =INT(B1) |
=SIGN(A1) |
-1, 0, or 1 depending on sign | =SIGN(B1) |
=SQRT(A1) |
Square root | =SQRT(B1) |
=POW(A1, B1) / =POWER(A1, B1) |
Exponentiation | =POW(2, 10) |
=MOD(A1, B1) |
Remainder of division (result takes the sign of the divisor, e.g. MOD(-7,3) → 2) |
=MOD(A1, 3) |
=EXP(A1) |
e raised to the power of x | =EXP(1) |
=LN(A1) |
Natural logarithm | =LN(8) |
=LOG(A1) / =LOG(A1, base) |
Base-10 log (single-arg), or log to a given base | =LOG(8, 2) |
=LOG10(A1) |
Base-10 logarithm | =LOG10(100) |
=PI() |
The constant π | =ROUND(PI(), 2) |
| Operator | Example |
|---|---|
+ |
=A1+B1 |
- |
=A1-B1 |
* |
=B1*C1 |
/ |
=A1/B1 |
^ |
=A1^2 (exponent) |
| Grouped | =(A1+B1)*C1 |
| Scientific notation | =1e3+1 → 1001 |
Combine functions and arithmetic freely:
=SUM(A1:A3)*1.1
=ROUND(AVERAGE(B1:B5), 2)
=A1+SUM(B1:B3)
Note: Function arguments must be cell references, ranges, or literal numbers — not inline expressions. Use
=ABS(A1)not=ABS(A1-B1).
| Error | Cause |
|---|---|
#ERR |
Invalid expression, divide by zero, or any other non-finite result (e.g. SQRT of a negative number) |
#NAME? |
Unknown function name |
A function that produces #ERR invalidates the whole formula, even when nested inside another function — =SUM(SQRT(-4), 5) is #ERR, not 5.
Hover over any result cell to see the original formula. Formulas are stored in the markdown — the plugin only changes how they're displayed.
When you click into a formula cell in Live Preview, the raw formula reappears for editing. Click away and the result renders again.
{{calc}}
| Category | Budget | Spent | Remaining |
|--------------|-------------|-------------|-------------|
| LLC setup | 500 | 200 | =B1-C1 |
| Dev tools | 200 | 99 | =B2-C2 |
| Marketing | 300 | 0 | =B3-C3 |
| **Total** | =SUM(B1:B3) | =SUM(C1:C3) | =SUM(D1:D3) |
{{calc}}
| Product | Price | Units | Revenue | Tax (10%) |
|---------|-------|-------|-------------|-------------|
| Widget | 29 | 12 | =B1*C1 | =D1*0.1 |
| Gadget | 49 | 7 | =B2*C2 | =D2*0.1 |
| Totals | | | =SUM(D1:D2) | =SUM(E1:E2) |
main.js, manifest.json, and styles.css from the latest release<your vault>/.obsidian/plugins/table-calc/| Command | Description |
|---|---|
Evaluate table formulas in this note |
Re-runs formula evaluation on all {{calc}} tables in the active note. Useful if a table doesn't update automatically. |
Access via Cmd+P (Mac) or Ctrl+P (Windows/Linux).
Formulas are stored as plain text in your markdown file — the plugin never modifies the source. A MutationObserver watches for rendered tables and evaluates any =formula cells on the fly. Only tables immediately preceded by a {{calc}} paragraph are processed; all others are ignored. In Reading view the cell's display is replaced with the computed result; in Live Preview the raw formula stays visible (tinted, since those cells are natively click-to-edit and rewriting their text would break Obsidian's cursor placement) and the result shows as a tooltip on hover instead.
This means your notes remain portable: open them anywhere and you'll see the raw formulas. Enable the plugin and you see the results.
npm test runs the automated suite: engine.test.js covers the formula engine directly, and main.test.js exercises processTable's DOM handling (via jsdom) — in particular, that Live Preview never rewrites a formula cell's text (Obsidian's Live Preview tables are natively click-to-edit, and CM6 maps cursor position by walking the cell's rendered text, so any change to it desyncs that mapping and corrupts editing).
test/example-vault-note.md is a manual fixture, not run by npm test. Copy it into any vault with this plugin installed to eyeball formula evaluation, error handling (#ERR/#NAME?), and Live Preview vs. Reading View rendering side by side — every Result cell should match the Expected cell next to it (Section 6 is deliberately the exception).
0=ABS(A1), not =ABS(A1-B1))1,234 (thousands separator), 5 apples (trailing text), and the literal text Infinity/NaN are all treated as text, not as the number they might resemble — they're excluded from COUNT. This is intentional: parseFloat in JavaScript would otherwise silently read 1,234 as 1 and the string "Infinity" as the number Infinity.SUM/AVERAGE treat them as contributing 0 (so a stray text cell skews a total or average rather than being skipped). MIN, MAX, MEDIAN, PRODUCT, STDEV, and VAR instead exclude non-numeric cells entirely, since a phantom 0 would distort those results far more severely (e.g. zeroing out an entire PRODUCT, or pulling a MEDIAN/STDEV toward a value no cell actually contains).2^53 (~9 quadrillion) lose precision, and results are snapped to 8 decimal places to absorb ordinary binary floating-point drift (e.g. 0.1+0.2 reliably shows 0.3, not 0.30000000000000004)ROUND rounds half away from zero (ROUND(2.5,0) → 3, ROUND(-2.5,0) → -3), matching spreadsheet conventions rather than JavaScript's native Math.round (which rounds -2.5 to -2)A2 always means "column A, row 2 of the table as it exists right now" — there's no concept of a formula "belonging" to a row. If you insert, delete, or reorder rows, formula text doesn't shift to compensate, so a formula can silently start pointing at the wrong cells. Re-check (or rewrite) formulas after restructuring a table. This is intentional: the plugin never modifies your markdown source, and auto-shifting references would require doing exactly thatTable Calc is designed for personal notes, budgets, and quick calculations — the kind of arithmetic you'd otherwise reach for a spreadsheet for. It is not intended for safety-critical, financial-regulatory, or any other high-stakes computation. Results are rendered in-browser using JavaScript's standard IEEE 754 floating-point arithmetic and are subject to the same rounding characteristics as any spreadsheet. Always verify critical numbers independently.
MIT