Table Calc

approved

by Benjamin Patterson

Adds formula support to markdown tables. Use =SUM(A1:A5), =AVG(B1:B3), =A1*B1, etc. in any table cell. - This plugin has not been manually reviewed by Obsidian staff.

2 stars1,655 downloadsUpdated 13d agoMIT

Table Calc — Obsidian Plugin

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.


Quick Start

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.


Column and Row Reference

LabelMeaning
A1Row 1, first column
B2Row 2, second column
A1:A5Range: column A, rows 1 through 5
A1:C32D range: columns A–C, rows 1–3

Supported Formulas

Functions

FormulaDescriptionExample
=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)

Arithmetic

OperatorExample
+=A1+B1
-=A1-B1
*=B1*C1
/=A1/B1
^=A1^2 (exponent)
Grouped=(A1+B1)*C1
Scientific notation=1e3+11001

Mixed

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 Values

ErrorCause
#ERRInvalid 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.


Formulas in Context

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.


Example: Budget Tracker

{{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) |

Example: Sales Table

{{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) |

Installation

From Obsidian Community Plugins

  1. Open Settings → Community Plugins
  2. Click Browse and search for Table Calc
  3. Click Install, then Enable

Manual Installation

  1. Download main.js, manifest.json, and styles.css from the latest release
  2. Create a folder: <your vault>/.obsidian/plugins/table-calc/
  3. Copy the three files into that folder
  4. In Obsidian: Settings → Community Plugins → reload → enable Table Calc

Commands

CommandDescription
Evaluate table formulas in this noteRe-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).


How It Works

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.


Testing

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).


Limitations

  • Column letters support A–Z (26 columns max)
  • Circular references are stopped at depth 20 and return 0
  • Formulas only evaluate in Live Preview and Reading view, not in Source mode
  • Function arguments must be cell references, ranges, or literal numbers — not inline expressions (use =ABS(A1), not =ABS(A1-B1))
  • A cell counts as numeric only if it's a complete, well-formed number. 1,234 (correctly-grouped thousands separator) is recognized and parsed as 1234, but 5 apples (trailing text), the literal text Infinity/NaN, and malformed groupings like 12,34 or 1,23,456 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.
  • A text cell (non-blank, not a formula, not a valid number) referenced directly in a formula or passed as a specific function argument — =A1, =A1+5, =ABS(A1), =ROUND(A1,2) — returns #ERR instead of silently treating the text as 0, matching Excel/Sheets' #VALUE! behavior for direct arithmetic on text. A blank cell is not text and still silently contributes 0. SUM, AVERAGE, MIN, MAX, MEDIAN, PRODUCT, STDEV, and VAR, on the other hand, skip non-numeric cells within a range instead of erroring or treating them as 0 — this matches how Excel and Google Sheets handle text inside a range argument to those same functions. A range member that's a formula resolving to a number counts as a number everywhere, including MIN/MAX/COUNT/MEDIAN/PRODUCT/STDEV/VAR.
  • Errors are contagious, like in a real spreadsheet: if a formula references another cell (directly, in a range, or as a function argument) whose own formula resolved to an error, the referencing formula also returns #ERR — it's never silently swallowed into 0 partway down a chain of formulas.
  • Numbers are standard JavaScript doubles (IEEE 754), the same numeric type spreadsheets like Excel use. Integers beyond 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)
  • References are positional, not tracked. 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 that

Disclaimer

Table 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.


License

MIT

For plugin developers

Search results and similarity scores are powered by semantic analysis of your plugin's README. If your plugin isn't appearing for searches you'd expect, try updating your README to clearly describe your plugin's purpose, features, and use cases.