The Cast toolkit

Follow your question further.

From a mortgage payment to a discovered equation. Explore every help category, see the syntax, and find a starting point.

Basics

Variables, operators, labels, flags, and sections -- the building blocks of every Cast model.

| Flags

Control how variables display and behave.

variable = value | flag

Control how variables display and behave. The | result flag promotes a variable to the Results section at the top of the output pane. If the variable uses if(), it shows as a Decision with a green/red pass/fail badge -- put the good outcome first in if() so the formatting is correct.

Keep full financial amounts with K, M, B or T

company_value = $3.2T | result

$3,200,000,000,000

Show in Results banner

profit = revenue - costs | result

Appears at top

Decision: use if() with | result to show a pass/fail badge

decision = if(irr > hurdle, "MEETS HURDLE", "BELOW HURDLE") | result

Shows green check when true, red X when false

Show in bar chart

revenue = price * quantity | chart

Visualized below

Hide from UI (use in formulas only)

temp = 100 | hidden

Invisible

Lock input (user can't edit)

pi = 3.14159 | locked

Read-only

Display as percentage

rate = 0.05 | percent

Shows as 5%

Waterfall chart (value creation bridge)

revenue = 500000 | bridge
cogs = 200000 | bridge
opex = 100000 | bridge
ebitda = revenue - cogs - opex | bridge | result

Waterfall: Revenue -> -COGS -> -OpEx -> EBITDA

Add | chart to any variable — it gets its own bar chart. Add | result to bring it to the top.

| Labels

Override the auto-generated label.

variable = value | Display Name

Override the auto-generated label. Snake_case names like "loan_term" already display as "Loan Term" automatically. Use labels when you want a short formula name or a label that differs from the variable name.

Short var name, pretty label

x = 100 | Purchase Price

UI shows "Purchase Price"

Auto-label (no | needed)

purchase_price = 100

UI shows "Purchase Price" automatically

# Sections

Group related inputs together with section headers.

# SECTION NAME

Group related inputs together with section headers.

Add section header

# REVENUE
price = 100
qty = 50

Groups inputs visually

Add divider line

---

Horizontal separator

> Visible Text

Add helper text that shows in the UI.

> Your message here

Add helper text that shows in the UI.

Guide users

> Enter your assumptions below

Shows in UI

// Comments

Add comments that are hidden from the UI (code-only).

// Your note

Add comments that are hidden from the UI (code-only).

Internal notes

// TODO: Add validation

Not visible in UI

Finance

Calculate cash flows, investment returns, loan payments, and savings growth.

present_value

How much a series of future payments is worth TODAY.

present_value(rate, periods, payment, [future], [type])

How much a series of future payments is worth TODAY.

Pay $1,000/year for 10 years at 5%. What's that worth today?

present_value = pv(0.05, 10, -1000)

$7,721.73

Same, but each payment lands at the START of the year

present_value = pv(0.05, 10, -1000, 0, "start")

$8,107.82 — a payment made sooner is worth more today

payment

How much you pay each period on a loan.

payment(rate, periods, principal, [future], [type])

How much you pay each period on a loan.

$500k mortgage at 6% annual (0.5% monthly) for 30 years

monthly_payment = pmt(0.06/12, 360, 500000)

-$2,997.75

Same loan, but the payment is due at the START of each month

monthly_payment = pmt(0.06/12, 360, 500000, 0, "start")

-$2,982.84 — paying earlier costs slightly less per payment

Change 360 to 180 — see how a 15-year mortgage compares. Change 500000 to 750000 to scale up.

future_value

How much your savings will grow to with regular contributions.

future_value(rate, periods, payment, [present], [type])

How much your savings will grow to with regular contributions.

Save $500/month for 10 years at 7% annual

retirement = fv(0.07/12, 120, -500)

$86,542.40

Same, but each deposit lands at the START of the month

retirement = fv(0.07/12, 120, -500, 0, "start")

$87,047.23 — depositing sooner earns an extra month of interest each time

net_present_value

What a series of future cash flows is worth today.

net_present_value(rate, cashflow1, cashflow2, ...)

What a series of future cash flows is worth today.

Project returns $1k, $1.5k, $2k over 3 years at 10% discount

project_value = npv(0.10, 1000, 1500, 2000)

$4,016.53

internal_rate_of_return

What percent return your investment generates.

internal_rate_of_return(cashflow1, cashflow2, ...)

What percent return your investment generates. First cash flow must be negative (your investment).

Invest $10k, get back $3k, $4k, $5k over 3 years

return_rate = irr(-10000, 3000, 4000, 5000)

8.9%

rate

Calculate interest rate - what rate do you need to reach a goal with fixed payments?

rate(periods, payment, present_value, [future], [type])

Calculate interest rate - what rate do you need to reach a goal with fixed payments?

$500k loan with $2,998/month payment for 30 years - what's the rate?

monthly_rate = rate(360, -2998, 500000)

0.5% (6% annual)

Math

Basic math operations: conditionals, rounding, roots, logs, and aggregations like sum and avg.

if

Conditional logic - choose different values based on a condition.

if(condition, true_value, false_value)

Conditional logic - choose different values based on a condition. Tip: if you flag an if() variable with | result, it shows as a Decision in the Results section with a pass/fail badge. Put the "good" outcome first and the "bad" outcome second so the green/red formatting is correct. If the answer IS the yes-or-no, skip if() and write the comparison on its own: beat_budget = revenue > budget shows "Yes" or "No".

Decision badge: shows green "MEETS HURDLE" or red "BELOW HURDLE"

decision = if(irr > hurdle_rate, "MEETS HURDLE", "BELOW HURDLE") | Decision | result

MEETS HURDLE (green badge in Results section)

Change irr > hurdle_rate to irr > 0.20 — raise the bar and watch the badge flip red.

case

Multi-branch conditional - check multiple conditions and return the first match.

case(cond1, val1, cond2, val2, ..., else_value)

Multi-branch conditional - check multiple conditions and return the first match. List the best outcome first and worst last; the Results banner auto-colors green-to-red by branch order. Also supports a readable multiline form. Also: switch()

Multiline form (preferred for readability)

outcome = case
  when moic > 2 => "Strong Return"
  when moic > 1.2 => "Moderate Return"
  else => "Underperform"
| result

Strong Return (green), Moderate Return (amber), or Underperform (red)

Single-line form

outcome = case(moic > 2, "Strong Return", moic > 1.2, "Moderate Return", "Underperform") | result

Same behavior as multiline form

Change moic > 2 to moic > 3 — the first branch gets harder to hit. Try adding a fourth when clause.

sum

Add multiple values together.

sum(value1, value2, ...)

Add multiple values together.

Total closing costs

total = sum(5000, 2000, 1500)

8500

prod

Multiply all values together.

prod(value1, value2, ...)

Multiply all values together. Works with individual numbers or arrays.

Compound growth over 3 years

total_growth = prod(1.05, 1.08, 1.03)

1.1687

count

Count how many values (length of a list or array).

count(value1, value2, ...)

Count how many values (length of a list or array). NOTE: this counts EVERYTHING, including blanks and text -- unlike Excel's own COUNT, which only counts numbers. That difference is why count() always exports to Excel as a plain number, never a live formula.

Count data points

n = count(10, 20, 30, 40)

4

sumproduct

Multiply corresponding elements of two arrays and sum the results (dot product).

sumproduct(array1, array2)

Multiply corresponding elements of two arrays and sum the results (dot product). Like SUMPRODUCT in Excel.

Weighted score calculation

sumproduct([0.3, 0.5, 0.2], [90, 80, 70])

81

avg

Calculate average (mean) of values.

avg(value1, value2, ...)

Calculate average (mean) of values.

Average monthly revenue

avg_monthly = avg(10000, 12000, 11000)

11000

Try avg(10000, 12000, 11000, 50000) — one outlier pulls the mean up. Compare with median().

min

Find minimum (smallest) value.

min(value1, value2, ...)

Find minimum (smallest) value.

Lowest price from options

lowest = min(100, 85, 120)

85

max

Find maximum (largest) value.

max(value1, value2, ...)

Find maximum (largest) value.

Highest monthly sales

peak = max(10000, 15000, 12000)

15000

abs

Absolute value - remove negative sign.

abs(value)

Absolute value - remove negative sign.

Distance regardless of direction

distance = abs(-50)

50

round

Round to specified decimal places.

round(value, decimals)

Round to specified decimal places.

Round to 2 decimal places

price = round(123.456, 2)

123.46

Change 2 to 0 for whole dollars. Change to -2 to round to the nearest hundred.

floor

Round down to nearest integer.

floor(value)

Round down to nearest integer.

Number of complete years

years = floor(4.7)

4

ceil

Round up to nearest integer.

ceil(value)

Round up to nearest integer.

Boxes needed (always round up)

boxes = ceil(4.2)

5

sqrt

Square root.

sqrt(value)

Square root.

Standard deviation calculation

std = sqrt(variance)

(depends on variance)

pow

Raise to power.

base^exponent

Raise to power. Use ^ notation — pow() is accepted but ^ is canonical.

Compound growth

future = 100 * 1.05^10

162.89

Change 1.05^10 to 1.05^30 — see compounding accelerate over time. Change 1.05 to 1.10 for 10% growth.

sin

Sine of an angle.

sin(angle)

Sine of an angle. Works in RADIANS by default; add "deg" for degrees. Also: cos, tan, asin, acos, atan.

Degrees (just add "deg")

height = sin(30 deg)

0.5

Radians (Cast knows pi)

height = sin(pi / 6)

0.5

Don't name a variable "deg" or "rad" — those are unit names and will collide. Use angle_deg.

cos

Cosine of an angle.

cos(angle)

Cosine of an angle. Radians by default; add "deg" for degrees.

Degrees

x = cos(60 deg)

0.5

tan

Tangent of an angle.

tan(angle)

Tangent of an angle. Radians by default; add "deg" for degrees. Inverses: asin, acos, atan (return radians).

Degrees

slope = tan(45 deg)

1

ln

Natural logarithm (base e).

ln(value)

Natural logarithm (base e).

Inverse of exponential growth

rate = ln(2)

0.693 (ln doubling)

A whole data column at once: one log per row

log_compute = ln(data.compute)
trend = pearson(data.year, log_compute)

A straight-line trend on the log scale means exponential growth

log10

Logarithm base 10.

log10(value)

Logarithm base 10.

Orders of magnitude

magnitude = log10(1000)

3

exp

e raised to power (e^value).

exp(value)

e raised to power (e^value).

Exponential growth

growth = exp(0.05)

1.051

concat

Join text and numbers into a string.

concat(value1, value2, ...)

Join text and numbers into a string.

Build message with formatting

msg = concat("Total: $", round(amount, 2))

Total: $1234.56

Calculus

Derivatives and integrals -- as plain English. Get the slope of a formula, the area under a curve, or the rate of change of your data.

derivative

The rate of change of a formula, written as a new formula (its slope).

derivative of FORMULA with respect to VARIABLE

The rate of change of a formula, written as a new formula (its slope). Handles full and partial derivatives -- with several variables, the others are held constant. Say "partial derivative of ..." if you prefer. Also: derivative("x^2", "x").

Slope formula of a parabola

slope = derivative of x^2 + 3*x with respect to x

2 * x + 3

Partial derivative (y held constant)

dz = partial derivative of x^2 * y with respect to x

2 * x * y

Change x^2 to x^3 — derivative becomes 3*x^2. Change 3*x to 5*x to see the constant carry through.

slope

The slope of a formula at one specific point -- a single number (the instantaneous rate of change).

slope of FORMULA at VARIABLE = VALUE

The slope of a formula at one specific point -- a single number (the instantaneous rate of change). Also: slope_at("x^2", "x", 4).

Slope of a parabola at x = 4

rise = slope of x^2 + 3*x at x = 4

11

Change 4 to 0 — slope at the bottom of the parabola is 3. Try a negative value to see the downward side.

integral

The area under a formula between two points (a definite integral).

integral of FORMULA from A to B

The area under a formula between two points (a definite integral). Add "with respect to VARIABLE" when the formula has more than one variable. Also: integral("x^2", 0, 3).

Area under a parabola from 0 to 3

area = integral of x^2 from 0 to 3

9

Distance from a speed formula (speed = 9.8*t)

distance = integral of 9.8 * t from 0 to 5

122.5

Change 3 to 6 — area roughly doubles. Change x^2 to x to see a triangle instead of a curve.

rate_of_change

The slope between each point of a data series -- a numeric (discrete) derivative.

rate_of_change(series)  or  rate_of_change(values, x_values)

The slope between each point of a data series -- a numeric (discrete) derivative. Velocity from position readings, marginal cost from a cost curve. Pass a second series for the x-axis spacing. Also: gradient(series).

Velocity from position readings

position = [0, 1, 4, 9, 16]
velocity = rate_of_change(position)

[1, 2, 4, 6, 7]

With explicit time spacing

velocity = rate_of_change(position, time)

slope using real time gaps

Replace [0,1,4,9,16] with [0,1,2,3,4] — velocity becomes constant. Try [0,2,8,18] for acceleration.

area_under

The total area under a data series using the trapezoidal rule -- a numeric integral of measurements.

area_under(series)  or  area_under(values, x_values)

The total area under a data series using the trapezoidal rule -- a numeric integral of measurements. Total distance from velocity, total energy from power. Pass a second series for the x-axis spacing. Also: trapezoid(series).

Total flow from per-step readings

flow = [0, 2, 4, 6]
total = area_under(flow)

9

With explicit time spacing

distance = area_under(velocity, time)

area using real time gaps

Replace [0,2,4,6] with [0,0,10,0] — a spike. Watch total area react to the sudden peak.

Summary Stats

Descriptive statistics for understanding your data: averages, spread, percentiles, and confidence intervals.

median

Middle value when data is sorted.

median(value1, value2, ...)

Middle value when data is sorted. More robust than mean for skewed data.

Median of 5 values

mid = median(10, 20, 30, 40, 50)

30

stdev

Sample standard deviation - measures how spread out values are from the mean.

stdev(value1, value2, ...)

Sample standard deviation - measures how spread out values are from the mean.

Spread of test scores

spread = stdev(85, 90, 78, 92, 88)

5.36

Replace the numbers with 89, 90, 91 — spread shrinks to near zero. Try 10, 50, 90 for high spread.

spread

How spread out are values relative to their average? Returns a percentage.

spread(value1, value2, ...)

How spread out are values relative to their average? Returns a percentage.

Price volatility

volatility = spread(100, 110, 90, 105, 95)

8% variation

standard_error

How precisely do you know the average? Smaller = more reliable.

standard_error(value1, value2, ...)

How precisely do you know the average? Smaller = more reliable.

Precision of mean estimate

precision = standard_error(98, 100, 102, 99, 101)

0.71

confidence

95% confidence interval for the mean.

confidence(value1, value2, ...)

95% confidence interval for the mean. Returns [lower, upper] bounds.

Confidence interval for measurements

bounds = confidence(98, 100, 102, 99, 101)

[98.04, 101.96]

percentile

Find the value at a given percentile.

percentile(p, value1, value2, ...)

Find the value at a given percentile. percentile(90, ...) = value below which 90% of data falls.

90th percentile salary

top10 = percentile(90, 50000, 60000, 70000, 80000, 120000)

104,000

deviation_from

How far is a value from the average, in standard deviations? Above 2 or below -2 is unusual.

deviation_from(value, mean, stdev)

How far is a value from the average, in standard deviations? Above 2 or below -2 is unusual.

Is 110 unusual when mean=100, stdev=10?

z = deviation_from(110, 100, 10)

1.0 (one std dev above — not unusual)

variance

Sample variance — the square of standard deviation.

variance(value1, value2, ...)

Sample variance — the square of standard deviation.

Variance of returns

var = variance(5, 10, 15, 20, 25)

62.5

Sig Tests

Compare groups and test relationships. Read the p-value, effect size, and assumptions together.

ttest

Two-sample t-test (Welch) - tests whether two groups have different averages.

ttest(group1, group2)

Two-sample t-test (Welch) - tests whether two groups have different averages. No assumption of equal variance.

Compare drug vs placebo response

ttest([5, 7, 8, 6, 9], [2, 3, 4, 3, 5])

{ p_value: 0.003, significant: true }

Change the second group to [5,6,7,6,8] — groups converge and p_value rises above 0.05 (no longer significant).

paired_ttest

Paired t-test - tests whether before/after measurements differ on the SAME subjects.

paired_ttest(before, after)

Paired t-test - tests whether before/after measurements differ on the SAME subjects.

Did blood pressure drop after medication?

paired_ttest([120, 130, 125], [110, 118, 112])

{ p_value: 0.02, significant: true }

Change the after values closer to before — watch mean_diff shrink toward zero and p_value rise.

anova_test

One-way ANOVA - tests whether 3+ groups have different averages.

anova_test(group1, group2, group3, ...)

One-way ANOVA - tests whether 3+ groups have different averages. Like a t-test but for multiple groups.

Compare 3 fertilizers on plant growth

anova_test([4,5,6], [7,8,9], [1,2,3])

{ f_statistic: 27.0, significant: true }

Add a fourth group [10,11,12] — watch the F-statistic respond to the wider spread between groups.

pearson

Pearson correlation - measures linear relationship between two variables.

pearson(x_values, y_values)

Pearson correlation - measures linear relationship between two variables. r = 1 (perfect positive), r = -1 (perfect negative), r = 0 (no relationship).

Correlation between study hours and test scores

pearson([1,2,3,4,5], [50,55,65,70,80])

{ r: 0.98, p_value: 0.003 }

Add a fourth group [10,11,12] — watch the F-statistic respond to the wider spread between groups.

chisquare

Chi-square goodness of fit - tests whether observed frequencies match expected frequencies.

chisquare(observed, expected)

Chi-square goodness of fit - tests whether observed frequencies match expected frequencies.

Is a die fair? (expected: equal frequencies)

chisquare([18,16,14,12,17,13], [15,15,15,15,15,15])

{ chi2: 2.0, significant: false }

mann_whitney

Mann-Whitney U test - non-parametric alternative to the t-test.

mann_whitney(group1, group2)

Mann-Whitney U test - non-parametric alternative to the t-test. Use when your data is not normally distributed or has outliers.

Compare satisfaction ratings (ordinal data)

mann_whitney([3,4,5,4,5], [1,2,3,2,2])

{ u_statistic: 22.5, p_value: 0.01, significant: true }

Change the after values closer to before — watch mean_diff shrink toward zero and p_value rise.

wilcoxon

Wilcoxon signed-rank test - non-parametric alternative to the paired t-test.

wilcoxon(before, after)

Wilcoxon signed-rank test - non-parametric alternative to the paired t-test. Use for before/after comparisons when data is not normally distributed.

Did pain scores improve after treatment?

wilcoxon([7,8,6,9,7], [4,5,3,6,4])

{ w_statistic: 0, p_value: 0.04, significant: true }

shapiro_wilk

Shapiro-Wilk normality test - checks if your data follows a bell curve (normal distribution).

shapiro_wilk(data)

Shapiro-Wilk normality test - checks if your data follows a bell curve (normal distribution). Run this before choosing between parametric and non-parametric tests.

Is my data normally distributed?

shapiro_wilk([2.3, 2.5, 2.7, 2.9, 3.1])

{ w_statistic: 0.99, significant: false } (looks normal)

Time Series

Analyze a series over time: smooth it, measure period-over-period change and growth, find repeating cycles, and reveal the strongest frequency with an FFT.

moving_average

Smooths a series by averaging each point with the ones before it.

moving_average(series, window)

Smooths a series by averaging each point with the ones before it. The first window-1 points are blank (not enough history yet).

3-point moving average

smoothed = moving_average(sales, 3)

NaN, NaN, 20, 30, 40

Change window from 3 to 7 — more smoothing, slower to react. Try 2 for almost no smoothing.

cumulative

Running total: each point is the sum of everything up to and including it.

cumulative(series)

Running total: each point is the sum of everything up to and including it.

Cumulative sales

total = cumulative(sales)

10, 30, 60, 100

Try cumulative(differences(sales)) — you get the original series back. Running total of changes = original.

differences

Period-over-period change: this point minus the previous one.

differences(series)

Period-over-period change: this point minus the previous one. First value is blank.

Change in sales

change = differences(sales)

NaN, 10, 10, 10

Try avg(differences(sales)) — the average period change. A perfectly flat series gives all zeros.

growth_rate

Percent change period over period, as a fraction (0.1 means +10%).

growth_rate(series)

Percent change period over period, as a fraction (0.1 means +10%).

Sales growth

growth = growth_rate(sales)

NaN, 1, 0.5, 0.33

Replace sales with a flat series like [100,100,100] — all zeros. Try doubling each step to see 100% growth.

lag

Shifts the series forward by N periods, filling the front with blanks.

lag(series, periods)

Shifts the series forward by N periods, filling the front with blanks. Use it to compare each point to one N steps earlier.

Last year same month

last_year = lag(sales, 12)

shifted 12 periods

Change 12 to 1 — previous month. Subtract lag(sales,1) from sales to get period-over-period change.

autocorrelation

How strongly the series resembles itself N steps later.

autocorrelation(series, lag)

How strongly the series resembles itself N steps later. Near 1 means a strong repeating pattern; near 0 means no relationship.

Does it echo itself?

repeat = autocorrelation(sales, 1)

0.95 (strong)

Try autocorrelation on a repeating series — near 1 means strong pattern. Try lag 12 on monthly sales.

detrend

Removes the best-fit straight line, leaving the wiggles around the trend.

detrend(series)

Removes the best-fit straight line, leaving the wiggles around the trend. A perfectly straight series detrends to zeros.

Strip the trend

wiggle = detrend(sales)

ups and downs only

Try on a straight line like [1,2,3,4,5] — residuals become all zeros. A curved series shows the bend.

frequency_spectrum

The strength of each cycle in the data (an FFT).

frequency_spectrum(series)

The strength of each cycle in the data (an FFT). A tall bar means a strong repeating cycle at that frequency. Plot it to SEE which cycles dominate.

See the cycles

spectrum = frequency_spectrum(signal)

plot it as a line

dominant_cycle

The length (in samples) of the strongest repeating cycle, found from the frequency spectrum.

dominant_cycle(series)

The length (in samples) of the strongest repeating cycle, found from the frequency spectrum. e.g. monthly sales with a yearly cycle returns ~12.

Find the cycle

period = dominant_cycle(sales)

12

Explore Data

Get to know your data before analyzing it. Summary stats, missing value checks, distributions, and scatter plots.

describe

Summary statistics in one line.

describe(column) or describe(data)

Summary statistics in one line. On a single column: count, mean, stdev, min, max, median, quartiles, missing. On the whole dataset: summary for every column.

Describe one column

stats = describe(data.yield)

{count: 100, mean: 42.5, stdev: 12.3, min: 10, max: 95, ...}

Describe entire dataset

overview = describe(data)

{temp: {count: 100, mean: 250, ...}, yield: {count: 100, mean: 42, ...}}

Try describe(data) on the whole dataset — you get a summary for every column at once.

missing

Count how many values are blank or NaN in a column.

missing(column)

Count how many values are blank or NaN in a column. Run this before analysis to know what you are working with.

Check for missing scores

blanks = missing(data.score)

3 (3 rows have no score)

Run this before any analysis — a column with 20 missing values will skew every stat. Clean first.

count_unique

Count distinct values in a column.

count_unique(column)

Count distinct values in a column. Low count (2-10) means categorical, high count means continuous. Helps you decide which analysis to use. NOTE: Excel has its own UNIQUE() that returns the LIST of distinct values, not a count -- Cast's "unique" alias still works, but count_unique is the honest name for what this actually returns.

Is status categorical?

n_status = count_unique(data.status)

3 (e.g., active/inactive/pending -- categorical)

Is revenue continuous?

n_rev = count_unique(data.revenue)

487 (many distinct values -- continuous)

Charts

Visualize your data with one line. Load data in the DATA tab, then use plot to create scatter, bar, line, area, or horizontal bar charts.

confusion_matrix

Counts classifications and automatically draws the chart in one line.

confusion_matrix(actual, predicted)

Counts classifications and automatically draws the chart in one line. No extra chart flag or plot line. Supply matching nonempty lists of class labels. Display rows are predicted classes; columns are actual classes. Each cell shows its count; the summary shows accuracy and sample count. Up to 50 classes; remove missing labels first.

Compare known answers with predictions

quality = confusion_matrix(["Pass","Fail"],["Pass","Pass"])

One correct classification and one mistake; 50% accuracy

Scientific Plots

An editable showcase of scatter, line, bar, area, histogram, box, violin, heatmap, contour, 3D scatter, a connected 3D path, surfaces and a confusion matrix.

Templates → Math, Science & Engineering → Scientific Plots

An editable showcase of scatter, line, bar, area, histogram, box, violin, heatmap, contour, 3D scatter, a connected 3D path, surfaces and a confusion matrix. Sample data is illustrative. Hover for values, drag 3D plots to rotate and use Play/Pause or the time scrubber for moving surfaces. The camera button saves an image.

Surface plots over time

motion = moving_surface("sin(x)*cos(y)*cos(t)",8,2)

A changing surface with fixed height and color ranges

box_plot

Compare spread, median and outliers with a box plot.

box_plot(values)

Compare spread, median and outliers with a box plot.

Try this editable example

spread_plot = box_plot([2,4,5,6,9])

An interactive scientific plot in Charts

violin_plot

Inspect the shape of a distribution with a violin plot and a box summary.

violin_plot(values)

Inspect the shape of a distribution with a violin plot and a box summary.

Try this editable example

density_plot = violin_plot([2,4,5,6,9])

An interactive scientific plot in Charts

heatmap

Color a rectangular grid of finite numbers.

heatmap(grid)

Color a rectangular grid of finite numbers. Columns are X; rows are Y.

Try this editable example

map = heatmap([[1,2],[3,4]])

An interactive scientific plot in Charts

contour_plot

Show equal-height contours in a rectangular numeric grid.

contour_plot(grid)

Show equal-height contours in a rectangular numeric grid.

Try this editable example

levels = contour_plot([[1,2],[3,4]])

An interactive scientific plot in Charts

scatter_3d

Inspect three measurements for each point.

scatter_3d(x,y,z)

Inspect three measurements for each point. Named lists label the axes. Hover for point details. Supply matching lists of finite numbers. Drag to rotate; use the camera button to download a PNG.

Try this editable example

points = scatter_3d([1,2],[3,4],[5,6])

An interactive scientific plot in Charts

path_3d

Draw 3D paths by connecting at least two samples in their original order.

path_3d(x,y,z)

Draw 3D paths by connecting at least two samples in their original order. Supply matching lists of finite coordinates. Drag to rotate; hover for the sample number. The path draws automatically.

Try this editable example

trajectory = path_3d([0,1,0],[0,0,1],[0,1,2])

An interactive scientific plot in Charts

surface_plot

Draw a surface from a formula using x and y.

surface_plot("formula", extent)

Draw a surface from a formula using x and y. Both axes run from minus extent to plus extent; default extent is pi. Drag to rotate.

Try this editable example

height = surface_plot("sin(x)*cos(y)",2)

An interactive scientific plot in Charts

moving_surface

Play a surface formula using x, y and t.

moving_surface("formula", seconds, extent)

Play a surface formula using x, y and t. Defaults: 8 seconds and extent pi. A fixed 31 × 31 grid and 33 time samples preserve the height and color scales. Supports pi/e, arithmetic and sin/cos/tan/exp/log/sqrt/abs/sinh/cosh/tanh. Other symbols, assignments and nonfinite heights are rejected. This displays sampled formulas; it does not solve differential equations.

Try this editable example

motion = moving_surface("sin(x)*cos(y)*cos(t)",8,2)

An interactive scientific plot in Charts

| chart

Two or more scalar values share one X axis.

value = expression | chart

Two or more scalar values share one X axis. Active and each saved scenario get their own chart with the same item order. Each bar keeps its own unit label; compare heights only for matching units. Single values compare scenarios. Flagged data series and forecasts with matching time and value units share one line chart by default. Each line has a different color and ends with its name and final value; narrow or crowded charts use a legend. Money labels show whole amounts, while calculations keep their cents.

Compare revenue and costs

revenue = $1000 | chart
costs = $400 | chart

Revenue and Costs on one X axis; save a scenario to add its chart.

chart

Visualize grid columns or computed lists.

chart columns [vs column] [| type]

Visualize grid columns or computed lists. Pipes choose the type: chart yield | box. Add a title after a pipe with single or double quotes: chart revenue | line | "12 Month Forecast". Forecast charts inherit month/year or named calendar labels. Use "vs" for an explicit X axis. Comma-separate multiple Y columns. A separate chart line groups selected series; | chart keeps default output grouping. Distributions take one list without vs and keep numeric bins. Default: scatter for "vs", bar otherwise. Also: plot, graph (same thing), and existing as aliases.

Scatter plot -- paste this sample data in the DATA tab to try it: temperature | yield -- 60 | 45 -- 70 | 52 -- 75 | 58 -- 80 | 63 -- 85 | 70 -- 90 | 74

chart yield vs temperature

Scatter chart showing temperature on X axis, yield on Y axis with trend visible

Bar chart -- sample data: region | revenue -- North | 175000 -- South | 145000 -- East | 120000 -- West | 85000

chart revenue | bar

Vertical bars, one per row, colored with the selected theme

Multi-series line -- sample data: quarter | revenue | profit -- Q1 | 100000 | 30000 -- Q2 | 120000 | 45000 -- Q3 | 160000 | 80000 -- Q4 | 200000 | 110000

chart revenue, profit vs quarter | line

Two smooth lines in different theme colors, labeled with their names and final values

Horizontal bar (good for long category names)

chart sales | hbar

Horizontal bars ranked largest on top, values labeled to the right

Area chart (line with fill)

chart revenue vs quarter | area

Smooth curve with subtle fill underneath

Distribution of one list

chart yield | box

Box plot of the list; also use | histogram or | violin

Change | bar to | line — same data, different shape. Add a second column: chart revenue, profit | bar for multi-series.

| animate (Animated Line Chart)

Add | animate (alias | play) to a line chart to get a time-playback animation.

chart col [, col] as line | animate

Add | animate (alias | play) to a line chart to get a time-playback animation. A Play/Pause button and a time scrubber reveal the curve point by point, with the y-axis pinned. Great for watching a forecast or ODE solution unfold over time.

Watch a forecast unfold

initial = 100
growth = 10%
forecast 10 years:
  balance starts at initial, then previous balance * (1 + growth)
chart balance as line | animate

Animated chart: press Play to watch the curve build year by year

Two coupled series animated together

chart revenue, profit as line | animate

Both curves animate together, y-axis stays fixed

Press Play and drag the scrubber to jump to any point. Add a second series (chart a, b as line | animate) to watch both unfold.

scatter

Scatter plot of two data columns.

scatter(x_column, y_column)

Scatter plot of two data columns. Prefer "chart Y vs X" for grid data -- scatter() is for computed arrays.

Using data proxy columns

my_scatter = scatter(data.temp, data.yield)

Scatter plot showing each data point

histogram

Distribution chart for one column.

histogram(column) or histogram(column, bins)

Distribution chart for one column. Each outlined bar has its count above it; the horizontal axis labels the measured amounts. See if data is normal, skewed, or bimodal. Auto-picks bin count or specify your own. Use the camera button to download a PNG.

Distribution of yields

dist = histogram(data.yield)

Histogram with auto-calculated bins

Fine-grained distribution

dist = histogram(data.yield, 20)

Histogram with 20 bins

Data Cleaning

Fix messy data before analysis. Remove blanks, fill in missing values, and strip outliers that skew your results.

drop_missing

Remove all blank, null, and NaN values from an array.

drop_missing(column)

Remove all blank, null, and NaN values from an array. Use this to clean a column before running statistics on it.

Clean a column before averaging

clean_scores = drop_missing(data.score)
avg_score = avg(clean_scores)

Average of non-blank scores

Wrap any stat in drop_missing() first — avg(drop_missing(data.score)) ignores the blanks.

fill_missing

Replace blank/NaN values with a number or strategy.

fill_missing(column, replacement)

Replace blank/NaN values with a number or strategy. Replacement can be a number, "mean", "median", or "zero".

Fill blanks with the column average

patched = fill_missing(data.score, "mean")
result = stdev(patched)

Stdev with blanks filled by mean

Fill blanks with zero

clean = fill_missing(data.revenue, 0)

Revenue with blanks as 0

Try fill_missing(data.score, 'mean') then stdev — the distribution barely changes vs. dropping rows.

remove_outliers

Drop extreme values using the IQR method.

remove_outliers(column) or remove_outliers(column, threshold)

Drop extreme values using the IQR method. Values below Q1 - 1.5*IQR or above Q3 + 1.5*IQR are removed. Adjust the threshold (default 1.5) to be more or less aggressive.

Remove extreme prices before analysis

clean_prices = remove_outliers(data.price)
avg_price = avg(clean_prices)

Average without extreme values

Strict outlier removal (threshold = 1.0)

tight = remove_outliers(data.price, 1)

Only values close to the middle 50%

Filtering

Slice your data by conditions. Keep rows that match, compare groups, or find the strongest predictors.

where

Filter an array to keep only elements where a condition is true.

values where condition > threshold

Filter an array to keep only elements where a condition is true. Works with data columns or any array. Like SQL WHERE but for arrays.

Yields where temperature exceeds 300

hot_yields = data.yield where data.temp > 300

[40, 50] (only rows where temp > 300)

Average of a filtered group

avg_hot = avg(data.yield where data.temp > 300)

45 (average of filtered values)

Compare two groups with a t-test

result = ttest(
  data.yield where data.group == "A",
  data.yield where data.group == "B"
)

{ p_value: 0.03, significant: true }

Change > 300 to > 200 — more rows qualify, mean shifts. Swap > for < to get the other group.

subset

Filter all columns at once and save as a new dataset.

subset(data, column > threshold)

Filter all columns at once and save as a new dataset. All columns keep the same rows. Use the result like data.* (e.g., hot.yield, hot.pressure), or filter that dataset again using its own columns.

Filter to high-temperature rows, then use all columns

hot = subset(data, data.temp > 300)
avg_yield = avg(hot.yield)
avg_pressure = avg(hot.pressure)

All columns filtered to rows where temp > 300

Split into groups and compare with t-test

group_a = subset(data, data.group == 1)
group_b = subset(data, data.group == 2)
result = ttest(group_a.score, group_b.score)

{ p_value: 0.03, significant: true }

Combine conditions with and / or / not

clean = subset(data, data.temp < 110 and data.yield < 60)
result = pearson(clean.temp, clean.yield)

Only rows that pass both checks, every column still lined up

Change the threshold to split the data differently — the filtered columns all move together.

top_predictors

Screen all numeric columns in your data against a target and rank them by correlation strength.

top_predictors(target_column)

Screen all numeric columns in your data against a target and rank them by correlation strength. Saves you from writing pearson() for every column manually.

Find what drives yield

drivers = top_predictors(data.yield)

[{column: "temp", r: 0.98}, {column: "catalyst", r: 0.95}, ...]

Find what predicts churn

churn_factors = top_predictors(data.churn)

[{column: "tenure", r: -0.82}, {column: "tickets", r: 0.71}, ...]

Change data.yield to any other numeric column — the ranking reshuffles to show its drivers.

Forecasts

Project values forward over time. Debt paydowns, compound growth, and multi-year financial models.

timed value

Apply an amount once, at a chosen forecast period; it is zero in other periods.

event = amount in year period

Apply an amount once, at a chosen forecast period; it is zero in other periods. Use the event row in an ordinary formula. Random timing: event = amount in any year from 1 to 18. Works with year, month, week, day or period. Timing must match the forecast and lie within its horizon. Also: at_period(amount, period), inside a forecast. Monte Carlo can override timing with event occurs in year 1 to 18, choosing one whole year once per simulation.

A crash and a later withdrawal

forecast 3 years:
  crash = -30% in year 1
  withdrawal = $100 in year 2
  balance starts at $1000, then previous balance * (1 + crash) - withdrawal

ending = final(balance) | result

Crash in year 1 only; withdrawal in year 2 only; ending balance $600

forecast

Create N calculated periods, starting at Month 1 or Year 1 after the first calculation.

forecast N years:
Value starts at base, then previous Value * 1.05

Create N calculated periods, starting at Month 1 or Year 1 after the first calculation. Tables and categorical charts inherit period names. For named months: forecast 12 months starting September 2026: (year optional). Add | include start after the colon to show Month 0 or Year 0. This starting point is display-only, excluded from totals, averages and financial functions. Missing starting values show a gap. A blank line ends the block. N can be literal or variable.

Named months with a separate starting point

forecast 12 months starting September 2026: | include start
revenue starts at $100, then previous revenue * 1.1

chart revenue | line | "12 Month Forecast"

Month 0 (August 2026): $100. September 2026: $110. Totals include only the twelve calculated months.

Literal number (blank line ends block)

forecast 5 years:
Rev starts at 100000, then previous Rev * 1.1
Profit = Rev * 0.2

// Alternative format (same calculation):
// Rev = 100000, then previous Rev * 1.1

Rev for year_1...year_5, Profit for year_1...year_5

Monthly forecast

forecast 12 months:
MRR = starting_mrr * 1.03^month

MRR for month_1...month_12

Variable for flexibility

hold = 7
forecast hold years:
Value starts at 1000, then previous Value * growth

Projects for 7 years (dynamic)

With line chart visualization

base = $1M
forecast 5 years:
revenue = base * 1.05^year | chart

Line chart showing revenue trend Year 1..5

Change 5 to 10 — more periods appear. Change 1.1 to 1.2 to watch compounding accelerate.

previous

Read the prior period value.

Value starts at initial_value, then previous Value * 1.05

Read the prior period value. The starting value is before period 1; the formula runs in every period. Also: Value = initial_value, then previous Value * 1.05 (same thing). Existing trailing ", starts at" syntax still works.

Debt paydown

Balance starts at 100000, then previous Balance - 10000

Before year 1: 100k. Year 1: 90k. Year 2: 80k.

final

Get the last value from a forecast series or list.

final(SeriesVariable)

Get the last value from a forecast series or list. Also: terminal() (same thing).

Final portfolio value

exit_value = final(Portfolio)

Value from final year

period accessor

Access a specific period from a forecast series.

year_N.Variable, month_N.Variable, week_N.Variable, or day_N.Variable

Access a specific period from a forecast series. The accessor prefix matches the forecast unit.

Get year 3 balance

y3 = year_3.Balance

Balance from 3rd year only

Get month 6 revenue

m6 = month_6.Revenue

Revenue from 6th month only

Constraints

Declare named pass/fail rules -- covenants, policy thresholds, engineering limits. Each renders as a green / red / amber chip in the results.

constraints

List named conditions that each render as a pass/fail card in the results.

constraints:
  expression
  expression | Label

List named conditions that each render as a pass/fail card in the results. A blank line or the next variable assignment ends the block. No | result flag and no boolean variable needed.

Unlabeled -- shows the expression

revenue = 500000
costs = 420000
constraints:
  revenue > costs

Green chip: "revenue > costs"

Labeled covenant checks

debt_ratio = 0.31
constraints:
  debt_ratio < 0.40 | Debt ceiling
  debt_ratio < 0.50 | Leverage OK

Two green chips: Debt ceiling, Leverage OK

Synonyms all work (covenants / rules / policies / requirements)

debt_to_ebitda = 6.1
covenants:
  debt_to_ebitda <= 5.5 | Leverage ceiling

Red chip: Leverage ceiling (breaches, 6.1 > 5.5)

Change debt_ratio from 0.31 to 0.45 — the chip flips red. Add a third constraint on the next line.

Model Backsolve

Describe a deal as known inputs (given) plus limits (require:), and Cast assembles the whole model from a trusted relationship library -- no AI in the math. Leave one input unknown (assume) and the Boundary Solver finds the value where a covenant hits its limit.

The given / require syntax works. Backsolve starters are currently hidden from the template picker.
given

Declare the known inputs of a deal -- its fixed numbers.

given:
  name = value
  name = value

Declare the known inputs of a deal -- its fixed numbers. One input per line inside the given: block. Cast derives everything else from the trusted relationship library (the physics). Pair with require: covenants and Cast assembles + checks the whole model. After a run, the "Full Model" panel in the results lets you type a new value over any of these inputs and press Cast again -- everything re-derives and the covenants re-judge, without editing your code.

An LBO deal that passes its leverage covenant

given:
  entry_ebitda = 50
  revenue_cagr = 0.10
  entry_multiple = 10
  debt_pct = 0.60
  interest_rate = 0.08
  amort_pct = 0.05

require:
  leverage_ratio < 6 | Max leverage

Covenant report: Max leverage PASS (5.45 < 6)

Change debt_pct from 0.60 to 0.75 — watch the covenant flip from PASS to FAIL.

assume

Leave exactly one input UNKNOWN.

assume knob

Leave exactly one input UNKNOWN. Pair it with a covenant that bounds a variable depending on it, and the Boundary Solver finds the knob value where the covenant sits at its limit -- "how much can this deal carry?". No arbitrary guess: it searches for the answer.

How much debt can the deal carry?

given:
  entry_ebitda = 50
  revenue_cagr = 0.10
  entry_multiple = 10
  interest_rate = 0.08
  amort_pct = 0.05

assume debt_pct
require:
  leverage_ratio < 6 | Max leverage

Boundary Solver: debt_pct = 66%

Change the covenant limit from < 6 to < 5 — the solver finds a tighter debt ceiling.

backsolve

Name the variables you want Cast to derive when they are not already implied by a covenant.

backsolve target, target

Name the variables you want Cast to derive when they are not already implied by a covenant. Comma-separated. Cast back-chains each target through the relationship library and shows the result.

Derive the leverage metrics directly

given:
  entry_ebitda = 50
  revenue_cagr = 0.10
  entry_multiple = 10
  debt_pct = 0.60
  interest_rate = 0.08
  amort_pct = 0.05

backsolve leverage_ratio, dscr

leverage_ratio = 5.45, dscr = 1.41

relationships

Define or override the physics Cast derives from.

relationships:
  target = expression

Define or override the physics Cast derives from. Built-ins (the trusted LBO library) are used automatically; add or replace formulas here. Synonyms: physics:, formulas:, automated calculations:. On Cast, a backsolve model auto-reveals the library into an "automated calculations:" block so you can see and edit every formula.

Add your own relationship, then backsolve from it

given:
  price = 100
  qty = 12

relationships:
  revenue = price * qty
backsolve revenue

revenue = 1200

Domain Libraries

Browse the built-in formula libraries Cast uses to assemble models. Each domain lists its trusted relationships -- the exact formulas behind Model Backsolve, with no AI in the math.

Leveraged Buyout 16 formulas
ebitda = entry_ebitda * (1 + revenue_cagr)

earnings after one year of growth

purchase_price = entry_ebitda * entry_multiple

what you pay: entry earnings times the multiple

debt = purchase_price * debt_pct

borrowed portion of the purchase price

equity = purchase_price - debt

your own cash in the deal

interest = debt * interest_rate

annual interest owed on the debt

amortization = debt * amort_pct

mandatory principal repaid each year

debt_service = interest + amortization

total debt payment due each year

leverage_ratio = debt / ebitda

turns of debt the company carries

interest_coverage = ebitda / interest

how easily earnings cover interest

dscr = ebitda / debt_service

earnings vs. all debt payments due

exit_ebitda = entry_ebitda * (1 + revenue_cagr)^hold_years

earnings the year you sell, after the whole hold period of growth

exit_ev = exit_ebitda * exit_multiple

what the company sells for at exit

remaining_debt = max(debt - debt * amort_pct * hold_years, 0)

debt still owed at exit, paid down at a steady rate

exit_equity = exit_ev - remaining_debt

what you walk away with when you sell

moic = exit_equity / equity

multiple of your cash back -- $1 in, this many dollars out

gross_irr = moic^(1 / hold_years) - 1

the annual return that would turn your investment into that multiple over the hold

Real Estate 15 formulas
gross_rent = monthly_rent * 12

a full year of rent at full occupancy

vacancy_loss = gross_rent * vacancy_rate

rent you lose to empty months

effective_income = gross_rent - vacancy_loss

the rent you actually collect after vacancies

noi = effective_income - operating_expenses

net operating income -- rent collected minus running costs

cap_rate = noi / purchase_price

the unleveraged yearly return, as a percent of price

gross_rent_multiplier = purchase_price / gross_rent

how many years of gross rent the price equals

price_per_sqft = purchase_price / square_feet

the price per square foot, for comparing properties

loan_amount = purchase_price * ltv

how much you borrow, set by the loan-to-value

down_payment = purchase_price - loan_amount

the cash you put down to buy

monthly_mortgage = loan_amount * (monthly_rate * (1 + monthly_rate)^num_payments) / ((1 + monthly_rate)^num_payments - 1)

the level monthly loan payment (standard amortization)

annual_debt_service = monthly_mortgage * 12

a full year of mortgage payments

dscr = noi / annual_debt_service

whether the rent covers the loan (lenders want above ~1.2)

annual_cash_flow = noi - annual_debt_service

the cash left in your pocket each year after the loan

cash_on_cash = annual_cash_flow / cash_invested

the yearly cash return on the actual cash you put in

breakeven_occupancy = (operating_expenses + annual_debt_service) / gross_rent

how full you must keep it just to cover the bills

Discounted Cash Flow 6 formulas
cost_of_equity = risk_free_rate + beta * market_premium

the return shareholders demand for taking on the risk (CAPM)

wacc = equity_weight * cost_of_equity + debt_weight * cost_of_debt * (1 - tax_rate)

the blended cost of all the money funding the business

present_value = cash_flow / (1 + wacc)^year

what a future payment is worth in today's dollars

terminal_value = final_cash_flow * (1 + growth) / (wacc - growth)

the value of all cash flows after the forecast, growing forever

terminal_value_today = terminal_value / (1 + wacc)^forecast_years

that far-future value pulled back to today's dollars

profitability_index = present_value / cash_flow

value created per dollar invested (above 1 is good)

Bond Math 6 formulas
coupon_payment = face_value * coupon_rate

the fixed dollar interest the bond pays each year

current_yield = coupon_payment / price

the income you earn as a percent of what you paid

capital_gain_per_year = (face_value - price) / years_to_maturity

the yearly pull toward face value as the bond matures

yield_to_maturity = (coupon_payment + capital_gain_per_year) / ((face_value + price) / 2)

an estimate of your total return if you hold to the end

price_change = -modified_duration * price * yield_change

how much the bond's price moves when interest rates shift

premium_or_discount = price - face_value

how much above or below face value the bond trades

Motion (Kinematics) 8 formulas
final_velocity = initial_velocity + acceleration * time

speed at the end after speeding up (or slowing) for a while

average_velocity = (initial_velocity + final_velocity) / 2

the mid-point speed when acceleration is steady

distance = initial_velocity * time + 0.5 * acceleration * time^2

how far you travel while steadily speeding up

momentum = mass * final_velocity

mass in motion -- how hard it is to stop

force = mass * acceleration

the push needed to produce that acceleration (Newton's second law)

kinetic_energy = 0.5 * mass * final_velocity^2

the energy stored in the motion

work_done = force * distance

energy delivered by pushing over a distance

power = work_done / time

how fast the work gets done

Heat & Thermal 6 formulas
heat_energy = mass * specific_heat * temp_change

energy needed to change a material's temperature

temp_diff = hot_temp - cold_temp

the temperature gap that drives heat to flow

thermal_resistance = thickness / (conductivity * area)

how strongly a wall resists heat passing through it

heat_rate = temp_diff / thermal_resistance

how fast heat flows through the wall, in watts

cooling_rate = cooling_coefficient * area * (hot_temp - ambient_temp)

how fast a hot object sheds heat to the room (Newton's cooling)

r_value = thickness / conductivity

insulation rating -- higher means it keeps heat in better

Electrical Circuits 7 formulas
voltage = current * resistance

the electrical push across a resistor (Ohm's law)

power = voltage * current

how fast the circuit uses energy, in watts

energy_used = power * time

total energy consumed while it runs

series_resistance = first_resistance + second_resistance

resistors in a line just add up

parallel_resistance = (first_resistance * second_resistance) / (first_resistance + second_resistance)

resistors side by side give a smaller combined resistance

capacitor_energy = 0.5 * capacitance * voltage^2

energy banked in a charged capacitor

charge = capacitance * voltage

how much charge the capacitor holds

Fluids 6 formulas
pressure = force / area

force spread over an area -- push per square metre

hydrostatic_pressure = density * gravity * depth

the squeeze from the weight of fluid above you

flow_rate = area * velocity

volume of fluid passing a point each second

dynamic_pressure = 0.5 * density * velocity^2

the pressure a moving fluid exerts by its motion

reynolds_number = density * velocity * pipe_diameter / viscosity

a dial: low means smooth flow, high means turbulent

buoyant_force = density * gravity * displaced_volume

the upward lift a fluid gives a submerged object

Beam & Structures 6 formulas
max_bending_moment = load * length / 4

the strongest bending the beam feels, at its centre

section_modulus = moment_of_inertia / half_depth

how much the beam's shape resists bending

max_stress = max_bending_moment / section_modulus

the peak pull on the material -- compare to its strength

max_deflection = (load * length^3) / (48 * elastic_modulus * moment_of_inertia)

how far the middle of the beam sags under the load

stiffness = (48 * elastic_modulus * moment_of_inertia) / length^3

how much load it takes to push the beam down by one unit

safety_factor = yield_strength / max_stress

how much headroom before the material fails (want it above 1)

Control Systems 6 formulas
time_constant = 1 / cutoff_frequency

how long the system takes to make most of a move

settling_time = 4 * time_constant

roughly how long until it stops moving and settles

natural_frequency = sqrt(stiffness / mass)

the speed the system would oscillate at on its own

damping_ratio = damping / (2 * sqrt(stiffness * mass))

how quickly wobbles die out (1 is just-right, below 1 overshoots)

damped_frequency = natural_frequency * sqrt(1 - damping_ratio^2)

the actual wobble speed once damping slows it down

percent_overshoot = 100 * exp(-damping_ratio * 3.14159 / sqrt(1 - damping_ratio^2))

how far past the target it shoots before settling, as a percent

Optimization

Set objectives, input ranges, and discrete choices. Inspect the best result and its certificate.

maximize

Name what to make as big as possible.

maximize variable

Name what to make as big as possible. Pair it with assume knobs (the dials Cast may turn) and Cast searches the whole range for the settings that achieve the best value -- the global optimum, with a certificate. The answer renders in its true shape: a number for one dial, a 2D map for two. Use minimize to make something as small as possible instead. Your formulas must be in a relationships: block (not plain assignments) for the optimizer to see them.

Find the price that maximizes profit

relationships:
  cups = 500 - 8 * price
  profit = price * cups - 200
assume price from 1 to 60
maximize profit

Optimal Answer: 7,612.50 at price = 31.25

Change the upper bound from 60 to 25 — the optimum shifts to fit the new constraint.

minimize

Make a result as small as possible -- cost, risk, waste.

minimize variable

Make a result as small as possible -- cost, risk, waste. Works exactly like maximize but in the other direction. Name two or more objectives (e.g. maximize profit AND minimize risk) and Cast returns the trade-off frontier: the set of best balances, since there is no single winner.

Two competing goals -> a trade-off frontier

relationships:
  profit = 100 - 2 * cost
  quality = cost
assume cost from 0 to 40
maximize profit
minimize quality

Pareto frontier: every point is best at some balance

assume ... from ... to

Declare a dial Cast may turn, and the range it may turn it within.

assume knob from low to high

Declare a dial Cast may turn, and the range it may turn it within. The optimizer searches this range for the best setting. List several on one line, comma-separated -- no hard limit on knob count. Formulas must be in a relationships: block -- plain variable assignments are not visible to the optimizer. Answer shape scales with knob count: 1 knob -> curve, 2 knobs -> heatmap, 3+ knobs -> 2D slices through the optimum for the widest-range pairs. (Bare "assume knob" with no range = Boundary Solver: Cast auto-searches up to 1,000,000 to find the covenant floor.)

Two dials -> a 2D answer with the optimum pinned

relationships:
  profit = 100 * price - 2 * price * price + 60 * promo - 3 * promo * promo
assume price from 0 to 50, promo from 0 to 20
maximize profit

Optimal Answer: 1,550 at price = 25, promo = 10

choose

Let Cast pick the best option from a fixed list -- a financing structure, a tier, an on/off switch.

choose name from [a, b, c]

Let Cast pick the best option from a fixed list -- a financing structure, a tier, an on/off switch. Cast re-optimizes for each option and keeps the winner. A yes/no switch is just a two-item list: choose pik from [on, off].

Pick the option that maximizes profit, alongside a dial

relationships:
  profit = base * units - 2 * units * units
assume units from 0 to 50
choose base from [10, 20]
maximize profit

base = 20, units = 5, profit = 50

Monte Carlo

Run thousands of simulations with random inputs to see the range of possible outcomes. Great for risk analysis.

event timing

Random event timing applies an amount in one whole forecast period chosen once per simulation.

event = amount in any year from first to last

Random event timing applies an amount in one whole forecast period chosen once per simulation. All periods have equal chances, including both endpoints. Also: event = amount in year first to last. An ordinary Cast shows one sampled path. Works with months, weeks, days or generic periods; bounds must fit the forecast. Both accounts can reference one event row to share the crash. For fixed ordinary timing, declare event = amount in year 10 and add event occurs in year first to last inside Monte Carlo.

Random crash timing with yearly withdrawals

forecast 18 years:
  market_crash = -30% in any year from 1 to 18
  balance starts at $400K, then previous balance * (1 + market_crash) - $10K

ending_balance = final(balance) | result

@montecarlo
runs: 1000
show: ending_balance

1,000 ending balances with one crash in each path

fat_tails

Define symmetric uncertainty with more extreme outcomes than a bell curve.

fat_tails(center, spread, degrees = 5)

Define symmetric uncertainty with more extreme outcomes than a bell curve. Spread is the standard deviation. Also: student_t(center, spread, degrees). Degrees defaults to 5; values closer to 2 create heavier tails. Degrees must exceed 2 so spread is defined. Call sample() for a fresh draw.

Returns with fat tails

stock_returns = fat_tails(7%, 4%)
yearly_return = sample(stock_returns)

A fresh return with 7% average and 4 percentage-point standard deviation

custom_distribution

Define your own possible numeric outcomes and their probabilities.

custom_distribution(values, probabilities)

Define your own possible numeric outcomes and their probabilities. Supply one nonnegative probability per outcome, totaling 100%. Omit probabilities to sample observed or historical values equally with replacement. Duplicate observations retain their frequency. Also: discrete(values, probabilities). Call sample() for a fresh draw. Resampling individual observations does not preserve the original historical order.

Manual return probabilities

stock_returns = custom_distribution([-30%, 7%, 20%], [10%, 70%, 20%])
yearly_return = sample(stock_returns)

A 10% chance of -30%, 70% chance of 7%, and 20% chance of 20%

Sample observed returns

observed_returns = [-10%, 5%, 12%]
stock_returns = custom_distribution(observed_returns)
yearly_return = sample(stock_returns)

Each observation has an equal chance on every draw

sample

Draw a fresh number from a named distribution.

sample(distribution)

Draw a fresh number from a named distribution. In a forecast, a sampled row draws once each period. Reference that row from both accounts to share the same market return. Monte Carlo repeats the entire path. An ordinary Cast shows one random path; it is not an expected-value forecast.

Fresh market returns for each of 18 years, repeated 1,000 times

stock_returns = bell_curve(7%, 4%)
starting_balance = $200K
annual_contribution = $7K

forecast 18 years:
  yearly_return = sample(stock_returns)
  balance starts at starting_balance, then previous balance * (1 + yearly_return) + annual_contribution

ending_balance = final(balance) | result

@montecarlo
runs: 1000
show: ending_balance

18 yearly draws per path; 1,000 ending balances

bell_curve

Define a reusable bell curve.

bell_curve(center, spread)

Define a reusable bell curve. Spread means one standard deviation, not a hard bound. Also: normal(center, spread). Call sample() to draw a number. Inside @montecarlo, existing normal() distribution lines still draw one input per run.

Define returns, then draw one

stock_returns = bell_curve(7%, 4%)
yearly_return = sample(stock_returns)

A return drawn from the defined bell curve

equal_range

Define a bounded range with equal probability throughout.

equal_range(minimum, maximum)

Define a bounded range with equal probability throughout. Also: uniform(minimum, maximum). Call sample() for a fresh draw.

Choose a crash year

crash_years = equal_range(0.5, 18.5)
crash_year = round(sample(crash_years))

One integer year from 1 through 18

likely_range

Define a bounded range peaked at the likely value.

likely_range(minimum, likely, maximum)

Define a bounded range peaked at the likely value. Also: triangular(minimum, likely, maximum). Call sample() for a fresh draw.

Define uncertain inflation

inflation_choices = likely_range(1%, 2.5%, 5%)
inflation = sample(inflation_choices)

A draw between 1% and 5%

skewed_positive

Define a positive, skewed distribution from its underlying log-space center and spread.

skewed_positive(log_center, log_spread)

Define a positive, skewed distribution from its underlying log-space center and spread. Also: lognormal(log_center, log_spread). These parameters describe logarithms, not the final arithmetic average. Call sample() for a fresh draw.

Define a positive multiplier

multipliers = skewed_positive(0, 0.3)
multiplier = sample(multipliers)

A positive random multiplier

@montecarlo

Monte Carlo simulation block - run your model thousands of times with random inputs and event timings to see possible outcomes.

@montecarlo

Monte Carlo simulation block - run your model thousands of times with random inputs and event timings to see possible outcomes. The block contains runs:, optional distribution or event timing lines, and show:. Cast re-runs your WHOLE model each simulation. Define calculations in the model ABOVE the block and list existing output variables in show:. Models using sample() or inline random timing need only runs: and show:.

Simulate investment returns with uncertain rate and growth

investment = -100000
cashflow = 30000
years = 5
rate = 8%
growth = 3%
final_cf = cashflow * (1 + growth) ^ years
profit = npv(rate, cashflow, cashflow, cashflow, cashflow, final_cf) + investment

@montecarlo
runs: 1000
rate = 8% +/- 2%
growth = 2% to 5%, likely 3%
show: profit, final_cf

Distribution histogram + summary stats for profit and final_cf

Change runs: 1000 to runs: 100 — output gets noisier. Try 10000 — smoother curve, slower.

@sensitivitymatrix

Sensitivity matrix - test every combination of two variables and see results in a 2D heatmap grid.

@sensitivitymatrix

Sensitivity matrix - test every combination of two variables and see results in a 2D heatmap grid. Click any cell to load those values. Also: @whatif, @sensitivity-matrix.

How does revenue change across price and quantity?

@sensitivitymatrix
rows: quantity = [30, 50, 70]
cols: price = [80, 100, 120]
show: revenue

3x3 heatmap grid of revenue for each combo

bell curve (+/-)

Bell curve (normal distribution) - most values near the center, fewer at extremes.

var = center +/- spread

Bell curve (normal distribution) - most values near the center, fewer at extremes. Use for inputs where you know the average and typical spread (interest rates, growth rates, market returns). Also accepts "plus or minus".

Interest rate centered at 8% with 2% spread

rate = 8% +/- 2%

Samples mostly between 4% and 12%

Same thing, written out

rate = 8% plus or minus 2%

Samples mostly between 4% and 12%

Classic syntax (also works)

rate = normal(8%, 2%)

Identical behavior

Change +/- 2% to +/- 5% — watch the confidence band widen as uncertainty grows.

range with peak (to/likely)

Three-point estimate (triangular distribution) - you specify the minimum, most likely, and maximum values.

var = min to max, likely mode

Three-point estimate (triangular distribution) - you specify the minimum, most likely, and maximum values. Use when you have expert judgment but not statistical data.

Growth rate: worst 2%, likely 3%, best 5%

growth = 2% to 5%, likely 3%

Samples concentrated around 3%

Classic syntax (also works)

growth = triangular(2%, 3%, 5%)

Identical behavior

Shift the likely: value toward min or max — watch the output histogram skew to follow it.

flat range (to)

Equal probability across a range (uniform distribution) - every value equally likely.

var = min to max

Equal probability across a range (uniform distribution) - every value equally likely. Use when you only know the bounds, not the shape.

Price could be anywhere between 80 and 120

price = 80 to 120

Samples evenly spread between 80 and 120

Classic syntax (also works)

price = uniform(80, 120)

Identical behavior

Narrow the range (e.g. 90 to 110) — output tightens too. Widen it and watch spread grow.

lognormal distribution

Always-positive, right-skewed distribution.

var = lognormal(mean, stdev)

Always-positive, right-skewed distribution. Use for quantities that cannot go negative and have occasional large values (stock prices, project costs, incomes).

Cost multiplier (always positive, sometimes high)

cost_mult = lognormal(0, 0.3)

Samples centered around 1.0, skewed right

Units

Physics and engineering units with automatic dimensional analysis. Multiply kg by m/s^2 and get Newtons.

Whole-currency display

Large amounts in cards, calculations and charts use K (thousands), M (millions), B (billions) or T (trillions).

revenue = $5.342M | result

Large amounts in cards, calculations and charts use K (thousands), M (millions), B (billions) or T (trillions). Forecast tables share a labeled unit such as Amounts in millions. Percentages and ratios stay unchanged; calculations always use the full amount.

Readable result, exact calculation

revenue = $5.342M | result
profit = revenue * 25%

Revenue: $5.34M. Profit: $1.34M. The actual profit remains $1,335,500.

convert

Convert between compatible units (length, mass, temperature, etc.).

convert(value, target_unit)

Convert between compatible units (length, mass, temperature, etc.).

Force in different units

force_lbf = convert(100 N, lbf)

22.48 lbf

Temperature conversion

temp_f = convert(20 degC, degF)

68 degF

Try convert(100 N, lbf) for force, or convert(37 degC, degF) for temperature.

Unit Math

Cast does dimensional analysis automatically - units multiply and divide correctly.

value1 unit1 * value2 unit2

Cast does dimensional analysis automatically - units multiply and divide correctly.

Force = mass × acceleration

force = 10 kg * 9.8 m/s^2

98 N (auto-converts)

Speed = distance / time

speed = 100 m / 10 s

10 m/s

dbm_to_w

Convert decibels-milliwatt (dBm) to watts.

dbm_to_w(dBm)

Convert decibels-milliwatt (dBm) to watts. Common in RF/telecom.

WiFi transmitter power

power = dbm_to_w(20)

0.1 (100 mW)

w_to_dbm

Convert watts to decibels-milliwatt (dBm).

w_to_dbm(watts)

Convert watts to decibels-milliwatt (dBm). Inverse of dbm_to_w.

Express 1 watt in dBm

level = w_to_dbm(1)

30

free_space_path_loss

How much signal weakens over distance at a given frequency, in dB.

free_space_path_loss(distance_km, freq_ghz)

How much signal weakens over distance at a given frequency, in dB.

5 GHz WiFi at 100 meters

loss = fspl(0.1, 5)

86.43 dB

link_budget

RF link budget - calculates received signal power.

link_budget(tx_power, tx_gain, rx_gain, path_loss)

RF link budget - calculates received signal power. Result = tx_power + tx_gain + rx_gain - path_loss.

Point-to-point microwave link

signal = link_budget(30, 20, 20, 120)

-50 dBm

AI Discovery

Fit curves, compare predictive models, and search for readable equations.

Machine learning and symbolic regression require the hosted ML service. AI features use credits.
Word-based text classification

Try Data → Sample datasets → SMS spam — training for 3,877 labeled messages from the video.

build model to predict spam

Try Data → Sample datasets → SMS spam — training for 3,877 labeled messages from the video. These SMS samples replace the older website classifier demo pairs. Run the one-line command below. Cast detects message text and automatically separates training and test rows. Spam and Legit appear in the matrix and predictions. Then load SMS spam — new messages for ten separate messages without answers. The fitted model and its word reader stay ready for reuse.

Learn to detect spam

build model to predict spam

Cast detects message text and selects word learning automatically.

Choose inputs

build model to predict cancel from note, monthly_spend, country

Words, numbers and category inputs share one saved classifier.

Override ambiguous text

build model to predict urgent from subject as text

Explicitly learn words in short or repeated subjects.

Classification only. Cast detects varied sentences from training rows and shows the selected text columns. Short/repeated names stay categories; as text overrides this for ambiguous text. Each text cell needs nonblank text. Vocabulary learns training rows only, including each validation fold. Need at least 200 labeled rows, including 20 of each outcome in training and test. Text + prediction speed includes word reading and predictions; loading, network and screen time remain excluded. Replace discovery with predict spam for rows 1 to 10 to reuse it.

Model prediction speed

The quality card shows milliseconds per result (batch average), total milliseconds and count beside accuracy or fit.

build model to predict target from inputs; predict target for rows 1 to 10

The quality card shows milliseconds per result (batch average), total milliseconds and count beside accuracy or fit. Training uses untouched test records; predict shows the current batch, while accuracy retains its original test scope. Includes prediction and probabilities; word-model summaries also include reading text. Excludes training, loading, network and screen time. Missing timing says Not available.

Measure new predictions

predict revenue for rows 1 to 10

Results show measured model time for ten predictions.

A single result run alone can take a different amount of time. Word-based models also show Text + prediction speed: word reading plus prediction/probabilities, excluding loading, transport and display.

Overall model quality

The trained-model banner shows Test accuracy for categories, or Prediction fit (explained variation, R²) and Prediction error (RMSE) for numbers.

build model to predict target from inputs

The trained-model banner shows Test accuracy for categories, or Prediction fit (explained variation, R²) and Prediction error (RMSE) for numbers. Scores use original held-out test rows. A negative fit is worse than predicting the average. Missing scores say Not available.

Train and inspect the overall model score

build model to predict revenue from spend

Overall model quality appears above the detailed results.

The original score remains after saving, loading or predicting new rows. It does not guarantee accuracy on new data.

predict

Reuse a trained model on new rows without retraining.

predict target for row 1
predict target for rows 1 to 10

Reuse a trained model on new rows without retraining. First run build model to predict, then replace that command with predict, load a new CSV in Data and click Cast. Keep the same input column names and units. The known outcome column is not required; extra columns are allowed.

Predict revenue for two new customers

predict revenue for rows 1 to 2

Each new row appears beside its prediction in Output.

Also: rows 1:10 (same thing). Save the Cast model to retain the trained model and results for another session. Export CSV downloads every requested prediction. Run build model to predict again only when you want to retrain. In Data, Sample datasets is the last toolbar action. Datasets stays visible and asks you to sign in for saved datasets.

| fit

Trigger automatic curve fitting.

variable = array_values | fit

Trigger automatic curve fitting. Cast tries polynomial, power law, exponential, logarithmic, and linear fits against all other array variables in the model.

Fit y against x automatically

x = [1, 2, 3, 4, 5] | hidden
y = [2.1, 4.0, 8.9, 16.2, 25.1] | fit

Ranked fit cards: R², RMSE, AIC per equation

Fit experimental readings

time = [0, 1, 2, 3, 4] | hidden
concentration = [100, 67, 45, 30, 20] | fit

Best fit shown in chart with curve overlay

Change y values to [2, 8, 32, 128] -- fits an exponential. Try [1, 4, 9, 16] for a parabola.

discover

AI-powered equation discovery.

discover target from predictor1, predictor2
    mode = auto | guided
    max attempts = N

AI-powered equation discovery. Streams iterations live — AI proposes equations, solver fits them, best candidates are ranked by R²/AIC. Use // comments above the block to give the AI domain hints.

Basic discovery

x = [1, 2, 4, 8] | hidden
y = [2, 8, 32, 128] | hidden
discover y from x

AI finds y = 2 * x^2 (or similar)

Guided mode with domain hint

// Arrhenius kinetics expected
discover rate from temperature
    mode = guided
    max attempts = 15

Pauses after each attempt for your approval

Change mode to guided -- pause after each iteration to approve or skip. Add a // domain hint above the block.

build model

Short form: build model target.

build model to predict target [from col1, col2, ... | from all]

Short form: build model target. Also: discover model target (same thing). Automatically trains and compares an explainable boosting model (EBM) and a linear model. Picks the best by AUC for categories or R2 for numbers, and shows which factors matter most. Omit "from" or use "from all" to use all columns except the target.

Churn prediction (all columns)

build model to predict churn

Uses all columns except churn as predictors. Verdict: EBM won. AUC 0.91.

Churn prediction (specific columns)

build model to predict churn from tenure, spend, tickets

Verdict: EBM won. AUC 0.91. Factors ranked by importance.

Cast splits before learning, compares EBM and linear using training folds, then tests on untouched records. Need at least 200 labelled rows, including 100 training and 100 test rows. Classification needs at least 20 per outcome in each set. Bad model means do not use its predictions. Passing supports exploration of similar records. Numbers and categories can be predictors; their mappings use training records only.

build equation

Short form: build equation target.

build equation to predict target [from col1, col2 | from all]
    max attempts = N

Short form: build equation target. Also: discover equation target (same thing). Find a readable equation using training records, then test it on records kept aside first. Output leads with a use verdict, AI analysis and a Prediction check chart. Omit "from" to use every other column.

Find spending equation (all columns)

build equation to predict monthly_spend

Best: spend = 12.3 * ln(tenure) + 45.6 (R2 = 0.94)

Read the test verdict first. Need 200 labelled records, at least 100 training and 100 test records; classification needs 20 per outcome in each set. Bad model means do not use predictions. Binary equations include a Confusion Matrix of untouched test records: correct negatives, caught cases, missed cases and false alarms at a 50% cutoff. Numerical equations use Prediction check instead. R² is not classification accuracy. Display uses 3 significant digits; Exact equation keeps calculation precision. Other equations tried shows training alternatives.

AI Research

Ask a question about your data and let the AI agent run a multi-step investigation automatically.

research_mode

Ask a question about your data.

Research Mode (guide)

Ask a question about your data. Each step states its goal, runs a calculation and shows Supported, Disproved or Inconclusive with the reason. Ask AI opens a question about that step; the assistant receives all saved calculations. The final research answer comes first, followed by evidence for each goal. Mermaid diagrams shrink to fit their frame; Enlarge opens a larger view. Upload a CSV or choose a sample from Research with AI.

Step 1: Click Research with AI, type your question

"What drives crop yield?"

Agent breaks this into testable criteria

Step 2: Agent proposes code, you click Run

result = top_predictors(data.yield)

temperature: r=0.64 (strong), humidity: r=0.37 (moderate)

Step 3: Agent digs deeper based on findings

hot = data.yield where data.temperature > 77
cold = data.yield where data.temperature <= 77
result = ttest(hot, cold)

p=0.00003 -- significant difference

Step 4: Agent builds a model when ready

build model to predict yield from all

Predictive model with R-squared and feature importance

Click Research with AI and type your question — the agent breaks it into testable steps. Crop yield and marketing allocation demos include saved, editable goals for zero credits. AI research calls begin after Start Research.

research_criteria

Research starts with 2-4 testable goals.

Research Criteria (guide)

Research starts with 2-4 testable goals. Edit, add or remove them before starting. Each goal gets a verdict and reason: Supported, Disproved or Inconclusive. Inconclusive means the available evidence cannot support or disprove the claim. A correlation alone does not prove a cause or the best spending decision.

Example criteria for "What drives crop yield?"

1. Is temperature the strongest predictor?
2. Is there a soil pH threshold effect?
3. Do humidity and rainfall contribute independently?

Each gets checked off as the agent finds evidence

auto_run

Manual mode pauses after each calculation and evidence assessment.

Auto-Run Mode (guide)

Manual mode pauses after each calculation and evidence assessment. Choose Next step to continue, or enable Auto-run. Pause and Turn off auto finish the current request and prevent later steps. Each step shows credits used; the shared balance sits at upper right. Stop rejects late replies. Repeated calculation errors and duplicate proposals stop automatic work.

Typical auto-run session

Check auto-run box -> agent runs 5-10 steps -> pauses when done

Review each goal and verdict; actual credits are shown for every step.

App Features

Read, edit, organize, share, and export your work.

Read images in Assistant

Paste a screenshot or photo, or attach PNG, JPEG or WebP through the paperclip.

Assistant → paste image → review → Send

Paste a screenshot or photo, or attach PNG, JPEG or WebP through the paperclip. Review or remove the preview before Send. Ask about a chart, document image or screenshot. Images remain in saved chats and follow-up questions. Nothing sends automatically. Choose an image-capable model such as Claude, GPT or Gemini.

Read a chart

Paste a chart, then ask: What changed between these two years?

The assistant receives the actual image and your question.

Up to ten images totaling 2 MB per chat request. Start a new chat when older images fill this limit. Paste DOCX images separately.

Read local files in Assistant

Paste CSV text, Excel cells or document text.

Assistant → paste or Attach file → review → Send

Paste CSV text, Excel cells or document text. Attach CSV, Excel, ODS, Word (.docx) or plain text. Copied files also paste when your browser supplies them. Filenames and sheet names stay in your editable draft. Nothing sends automatically. DOCX reads text and tables, not images; save old .doc files as .docx or paste their text.

Read copied spreadsheet cells

Paste cells, then ask: Which items have the highest amount?

The assistant receives the pasted rows and columns.

Up to ten files totaling 5 MB, including expanded archives; at most 200,000 draft characters. Failed reads keep your draft.

Inspect assistant thinking

Open the caret to see readable thinking returned by the selected provider.

Thinking or Thought → caret

Open the caret to see readable thinking returned by the selected provider. It is saved with that reply on this browser. The connecting orb animates while a reply runs and becomes still with reduced motion. Some providers do not return readable thinking; Cast says so when the pending panel is opened.

Inspect a reply

Click Thought above the answer

The returned trace expands as plain text

Named sharing addresses

Signup asks for a chosen public username and optional full name.

Share → choose an owner and model name → Create link → Copy link

Signup asks for a chosen public username and optional full name. Personal links use cast.is/username/model-name. Organization administrators can choose cast.is/org/organization/model-name. Names only need to be unique within their owner. Email stays private. Old usernames and existing random links keep working.

Name a personal model

Username: russell; model address name: acquisition-model

cast.is/russell/acquisition-model

Gmail and other known personal providers default to Personal. A saved link appears before you copy it. Anonymous sharing retains temporary random links.

Themed notices and confirmations

Data replacement, clearing and deletion use a modal that follows your theme.

Read the confirmation → Cancel or Continue

Data replacement, clearing and deletion use a modal that follows your theme. Cancel leaves the existing data intact. Assistant and voice errors use the same modal instead of browser alerts.

Keep the current dataset

Data → Clear → Cancel

Your data stays in the grid

Escape cancels. Requests wait their turn so one notice cannot erase another.

Join your email-domain organization automatically

Verified company email domains get one organization automatically.

Verify your email → join its domain organization

Verified company email domains get one organization automatically. Accounts with the same exact company domain join as members. Known personal providers such as Gmail stay personal. Your personal models stay private. Changing your verified company domain removes access to the old domain organization.

Two verified accounts with the same domain

alex@company.com and sam@company.com

Both join the automatic company.com organization as members

No domain approval is needed. Subdomains are separate organizations. Joining does not grant administrator permissions, publish personal models or change your plan.

Private collaboration

A paid owner saves a model, then invites a verified Cast account by email.

Share → Collaborate → Invite editor

A paid owner saves a model, then invites a verified Cast account by email. The recipient accepts from Load. Free accounts can edit invited models. Edits reach others after Save. Only the owner manages access.

Work on a budget together

Save → Share → Collaborate → enter colleague email

Your colleague accepts and edits the same saved model

Stale saves keep your draft. Compare latest, Load latest or Save copy. Private trained-model handles and server datasets must be removed before inviting; inline imported data is supported.

CSV upload

Import your CSV, or click Data → Sample datasets.

Data → Import CSV → Editor → Cast

Import your CSV, or click Data → Sample datasets. Website phishing — training has 800 website records with answers; Website phishing — prediction has six separate records without answers. Train first, then load the prediction sample to reuse that model. 1 means phishing; 0 means legitimate. Historical input codes describe security checks, not raw measurements. Marketing and crop samples are also available. Cast asks before replacing populated data.

Analyze a loaded column

total_sales = sum(sales)

The total appears in Output; imported rows remain in Data

The notice disappears after four seconds. Hovering or focusing its close button keeps it visible.

Minimize data instructions

After loading data, the panel below the editor lists column names and commands to copy.

Editor: Data Grid Commands → Minimize → Show instructions

After loading data, the panel below the editor lists column names and commands to copy. Click its full header to minimize it to one bar and give your model more room. Click Show instructions to reopen it. Your grid and model stay unchanged.

Make room to write

Load data, switch to Editor, click Minimize

Instructions fold to one bar; Show instructions reopens them

Enter or Space toggles the focused header. Cast remembers your choice between visits.

Find real data with the assistant

Find data covers websites, APIs and public datasets.

Ask for a dataset or a table from a public page

Find data covers websites, APIs and public datasets. CSV, tab-separated text, JSON, spreadsheets, GitHub file pages, download links and simple page tables load directly without extraction credits. Other page tables use Firecrawl; Alexandria supplies provider datasets. Every web search uses Exa. Verified rows load into Data with editable row labels and a source receipt. Cast asks before replacing existing data. CSV uploads and spreadsheet paste preserve quoted multiline cells and blank values. Follow-up choices use the current model and add no AI calls.

Find data to analyze

Ask: "Find an official population dataset and prepare a small table for Data."

A source-backed table ready to load into the Data tab

Give the page URL, desired columns and number of rows. Cast has no fixed row-count cap; source page limits, prices and response-size limits still apply. Server keys and provider access are required. Missing data is never invented.

Terms of Service and Privacy Policy

The rules for using Cast, and what Cast does with your data.

Terms and Privacy links in the site footer and under the sign-in form

The rules for using Cast, and what Cast does with your data. Your saved models stay yours. Cast keeps the questions you ask its AI features and the models it generates, and uses them for analysis and to train AI models. Cast does not sell personal information.

Read what Cast collects and why

Open cast.is/privacy

What is collected, who it is shared with, and how to ask for deletion

Questions or deletion requests: russell@zavient.com.

Clear editor (+ New)

Wipes the editor and results back to a blank slate.

+ New button (editor toolbar)

Wipes the editor and results back to a blank slate. Clears code, scenarios, overrides, loaded data, and research state. Use it when you want to start a brand-new model from scratch without picking a template.

Start fresh

Click "+ New" in the editor toolbar

Editor empty, results gone, ready for a new model

Want a starting point instead? Click the smaller "Templates" button right next to + New.

Diagrams in AI answers

Ask the assistant to draw a flow or relationship.

AI assistant: Diagram → Enlarge → Close

Ask the assistant to draw a flow or relationship. Mermaid blocks render as diagrams in chat, with no Paste button; text diagrams also work. Replies use plain English and useful emoji signposts. Every model starts at medium effort for chat, model generation and troubleshooting; it can adjust effort for a harder next call using supported provider controls, which uses credits. Stop replaces Send while a reply is running, so you can cancel, correct your prompt and resend.

Ask how something works

Ask the assistant: "How does a forecast with previous and starts at work?"

A bold one-line answer, a few bullets, and a small ↓ diagram of period 1 feeding period 2

Ask the assistant to "draw it". Diagrams follow your theme and Enlarge opens a larger view. Rejected model code gets three repair retries with the exact Cast errors. Each repair uses credits. Copy debug retains rejected code and every attempt, including after reopening the chat.

Personality and memory

Save your background, preferences and how Cast should talk.

Assistant header: Personality and memory

Save your background, preferences and how Cast should talk. Details stay with your account on this browser across reloads and new chats. Say Remember that… in chat to save a preference without another AI call. Review, edit or remove details in the same window. Cast uses useful emoji and Mermaid diagrams on its own when they help.

Remember a useful preference

Remember that I prefer monthly forecasts

A saved preference you can review or remove in Personality and memory

Click Save preferences to keep changes. Cancel leaves your saved details unchanged.

Guide beside your results

The Guide opens as a column beside your results, so you can read a lesson, type it into the editor and see the result without closing anything.

Guide button (results toolbar)

The Guide opens as a column beside your results, so you can read a lesson, type it into the editor and see the result without closing anything. Close it and open it again and it comes back to the same spot, even after a reload.

Read and build side by side

Click "Guide", scroll to a lesson, copy an example into the editor, Cast

Editor, results and Guide all visible at once

Want more room to read?

Click the expand arrows at the top of the Guide

The Guide covers the results area; click again to put it back beside them

Beside or expanded is remembered between visits.

Theme Switcher

Change the app's color theme -- click the icon to pick one, or press Ctrl+D to cycle through them.

Paint-bucket icon (toolbar) or Ctrl+D to cycle

Change the app's color theme -- click the icon to pick one, or press Ctrl+D to cycle through them. Your choice is saved and changes chart colors, the wand, contrails, browser favicon and assistant icons. Cloud uses a brown wand, gold trails and an ivory favicon; other themes have matching colors. Phone and installed desktop icons keep gold and brown. Assistant text is readable as each word arrives.

Pick a theme

Click the paint-bucket icon in the toolbar

Theme picker opens; choose one to apply it instantly

Ctrl+D cycles to the next theme without opening the picker.

Templates

Opens worked examples for finance, business, science and backsolve.

Templates button (editor toolbar)

Opens worked examples for finance, business, science and backsolve. The starter and every personal-finance/finance-business worked template include forecast tables and charts. Future answers come from their forecast rows: loan payoff, savings, rent/buy equity, earnings, recurring revenue, online sales, marketing, real estate, acquisitions and cash planning.

Pick a template

Click "Templates" in the editor toolbar

Template picker opens; choose one to load it into the editor

Choose SaaS Growth and click Cast: month 1 starts at $50,000, adds 20% growth and removes 5% churn, ending at $57,500. Change Projection Period to adjust its months. Marketing repeats a constant monthly plan. Click "+ New" for a blank editor.

Keyboard Shortcuts

Speed up your workflow with keyboard shortcuts.

Cmd+Enter / Ctrl+Enter

Speed up your workflow with keyboard shortcuts. Available anywhere in the app.

Cast (evaluate) your model

Cmd+Enter (Mac) or Ctrl+Enter (Windows)

Evaluates model instantly

Save model

Cmd+Shift+S (Mac) or Ctrl+Shift+S (Windows)

Saves to cloud

Jump to section

Ctrl+Shift+1 through Ctrl+Shift+9

Scrolls to that section in results

Try Ctrl+Enter right now — same as the Cast button. Ctrl+Shift+S saves without reaching for the mouse.

Share Link

Turn the model into a link anyone can open in a browser — no install and no account on their end.

Share → Share Snapshot / Share View / Embed

Turn the model into a link anyone can open in a browser — no install and no account on their end. If the model is saved to your account the link follows it, so a formula you fix today reaches whoever is holding yesterday's link. Share an unsaved draft instead and the link keeps a frozen copy, because there is no saved model behind it to follow. The page tells the reader which one they have: Live means the numbers track yours, Snapshot means they are a copy from the moment you shared.

Share a saved model

Share → Share Snapshot

Choose a model address, then Create link. A Snapshot freezes the source you share.

Share an unsaved draft

Share → Share Snapshot without saving first

A named link marked Snapshot keeps a frozen copy. Anonymous links retain a random /m/<slug> address.

Freeze a link on purpose

Share -> Share Snapshot

A Snapshot keeps the source as shared. Visitors can edit code and click Cast without an account. Sign up to save edits keeps their own copy, including changed inputs. Signed-in visitors choose Save a copy.

Existing Live links keep following their saved source. Share Snapshot freezes new public code shares. Collaborate invites private editors. Share View hides code (Pro); anonymous links expire after 7 days, paid links are permanent.

Excel Export

Download your model as a formatted .xlsx workbook.

Share -> Export to Excel

Download your model as a formatted .xlsx workbook. Every number is there, and each calculation is a live Excel formula pointing at the cells above it — change an input and the whole sheet recalculates. Inputs are blue, calculations are black, results are boxed. Scenarios, your data grid, forecasts, what-if grids, risk simulations and rule checks each get their own tab, plus one holding the Cast source.

Export a model with scenarios

price = 100
revenue = price * 50
profit = revenue * 0.3 | result

@scenario "Bull"
price = 150

Model tab with =B7*50 style formulas, plus a Scenarios tab: Base case and Bull side by side

Open the file and click a calculated cell — the formula bar shows the Excel formula, so a spreadsheet person can audit the model without learning Cast. npv is rewritten as cash_flow_today + NPV(rate, later_flows) because Excel's NPV discounts every argument by a period; written that way it lands on Cast's number exactly. The few functions Excel genuinely cannot express (irr, percentile, stock, fetch) export their value and say so in the Notes column, rather than writing a formula that would give a different answer.

Dependency Trace

See what feeds into a variable (upstream) and what it affects (downstream).

Click trace icon on any variable

See what feeds into a variable (upstream) and what it affects (downstream). Shows a DAG (directed acyclic graph) visualization of the full formula chain.

Trace a profit calculation

revenue = 1000
costs = 400
profit = revenue - costs | result

profit depends on: revenue, costs. Nothing depends on profit.

Click the trace icon on any result variable — see its full dependency chain light up.

Version Stack

Save snapshots of your model, browse history, restore any version, and view line-level diffs showing exactly what changed.

Version button (top bar)

Save snapshots of your model, browse history, restore any version, and view line-level diffs showing exactly what changed. Each model has its own history: New, templates and copied models start separate histories; reload keeps the current model's versions. Draft versions follow their model on its first account save. The toolbar names the version you are on and says "(edited)" as soon as you change something. An unsaved draft keeps its history in this browser; a model saved to your account keeps it beside the model, so everyone working on that model sees each other's versions and who saved each one.

Save a version before making changes

Click version icon -> Save Version -> "Before restructuring"

Snapshot saved. Restore anytime.

Save a version before a big edit — then experiment freely. Restore in one click if it goes wrong.

Scenario Clone

One-click duplicate of any saved scenario.

+ button on scenario card

One-click duplicate of any saved scenario. Creates a copy with "(copy)" appended to the name, ready for editing.

Clone Bull Case to create a variant

Click + on "Bull Case" scenario card

Creates "Bull Case (copy)" with same overrides

Build one strong scenario, then clone it -- tweak the copy instead of rebuilding from scratch.

Variable Search

Filter variables by name in the results pane.

Search box in results pane

Filter variables by name in the results pane. Useful for large models with many variables.

Find all revenue-related variables

Type "revenue" in search box

Shows only variables containing "revenue"

In a big model, type a few letters -- the results pane narrows to just the matching variables.

Table of Contents

Section headers (# NAME) automatically create a sticky navigation bar at the top of the results pane.

# Section headers auto-generate TOC

Section headers (# NAME) automatically create a sticky navigation bar at the top of the results pane. Click any pill to jump to that section.

Auto-generated TOC from sections

# REVENUE
price = 100
qty = 50

# COSTS
cogs = 200
opex = 100

# PROFIT
profit = price * qty - cogs - opex | result

Pill bar: [REVENUE] [COSTS] [PROFIT] — click to jump

Add a # SECTION header above a group of lines -- a clickable jump pill appears in the results nav.

| bridge (Waterfall Chart)

Add the | bridge flag to variables that form a value creation chain.

variable = value | bridge

Add the | bridge flag to variables that form a value creation chain. Cast renders a waterfall chart showing how values build from first to last. The first and last bars show totals; middle bars show incremental changes.

EBITDA bridge

revenue = $1M | bridge
cogs = $400K | bridge
gross = revenue - cogs | bridge
opex = $200K | bridge
ebitda = gross - opex | bridge | result

Waterfall: Revenue -> -COGS -> Gross -> -OpEx -> EBITDA

Tag each line of a value chain with | bridge -- the waterfall builds from first total to last.

Voice Mode

Talk to Cast instead of typing.

Off / Speak / Converse (assistant header)

Talk to Cast instead of typing. Three modes: Off (text only), Speak (Cast reads replies aloud), Converse (mic fills the chat box and auto-sends when you stop talking).

Speak mode — Cast reads every reply aloud

Click the Off / Speak / Converse control -> select Speak

Cast reads its next reply using your device text-to-speech

Converse mode — hands-free Q&A

Select Converse -> tap the mic -> speak your question

Your words appear in the chat box and send automatically when you stop talking

Switch to Converse, tap the mic, and ask Cast out loud -- it sends when you stop talking.

Credits

Monthly AI credits stays visible above chat, with remaining out of total, a bar, percentage remaining and reset date.

Monthly AI credits → remaining / allowance

Monthly AI credits stays visible above chat, with remaining out of total, a bar, percentage remaining and reset date. Each answer states its actual credit cost. AI chat uses model-weighted credits; searches and research calls also use the balance. Free includes 60 credits a month; Pro includes 1,000, and Teams includes 1,000 per seat. Used in this chat counts this conversation.

Check your balance

Open the assistant panel, pick a model, then click Model and usage to minimize it. Click again to show the details. Your selection and layout are kept.

Monthly AI credits shows "720 of 1,000 credits left", "72% remaining" and a reset date, even when minimized. An answer receipt says "This answer used 20 credits".

New chats start with Opus; GPT Sol is second in the model picker. Saved chats keep their model choice. Each model receives its supported output allowance, including reasoning and the visible reply. The context meter shows that allowance. Switching to a cheaper model makes your credits last longer. An administrator-granted account exception shows Unlimited; ordinary plan limits remain unchanged.

| chart on Forecast (Line Chart)

Add | chart to forecast formulas to show their trends.

forecast N days|weeks|months|years:\n  variable = expr | chart

Add | chart to forecast formulas to show their trends. Lines with matching time and value units combine in one chart by default. The title describes the time span. Each line has a different color and ends with its name and final value; narrow or crowded charts use a legend. Currency labels show whole amounts, while the math keeps cents. Currency and percentage lines, or different time units, keep separate charts so their scales remain clear.

Revenue growth line chart

base = $1000000
forecast 5 years:
  revenue = base * 1.05^year | chart

5-year forecast with the Revenue line ending at $1,276,282

Monthly growth line chart

base = $100000
forecast 12 months:
  mrr = base * 1.02^month | chart

Line chart: Month 1 -> Month 12

Combined forecast lines

base = $1000000
margin = 30%
forecast 5 years:
  revenue = base * 1.05^year | chart
  profit = revenue * margin | chart

One 5-year forecast chart with Revenue and Profit in different colors, each labeled at its final value

Add | chart to a forecast line -- the trend redraws every time you change the growth rate.

Autocomplete

As you type, the editor suggests matches: your own variables first (with their definitions), then Cast functions, @ directives, and the flags after a |.

Type two letters in the editor and pick from the list

As you type, the editor suggests matches: your own variables first (with their definitions), then Cast functions, @ directives, and the flags after a |. Press Enter to accept; a function arrives with its brackets and your cursor inside. Ctrl+Space asks for suggestions after one letter. It stays quiet inside comments, section titles and text. The Autocomplete On/Off button in the editor toolbar switches it, and Cast remembers your choice.

Finish a name you already defined

revenue = 100
profit = rev

The list shows revenue (your variable) first; Enter completes it

Turn it off with the Autocomplete button in the editor toolbar if you prefer typing everything yourself.

Click-to-trace in editor

Click a variable name in the editor, or click a result card, a variable row, or a forecast row label, and every place that name is used lights up in the editor.

Click any variable name in the editor, or any result; hover a name for its value

Click a variable name in the editor, or click a result card, a variable row, or a forecast row label, and every place that name is used lights up in the editor. The names it is made from light up in a second color on its defining line, so you can read "X = A * B" at a glance. Hover any name in the editor for a small card with its defining line and current value. Click anywhere else to clear.

See where a number comes from

Click material_payments in the editor

Every material_payments lights up; material_purchases and purchases_lag_1 light up in blue on its line

Works on every name in the file, including rows inside a forecast block.

Stock Data

Pull live stock prices, earnings, revenue, and historical data directly into your model.

Market-data functions require an FMP key. Custom APIs use their own connector settings.
stock

Current stock price in dollars.

stock('SYMBOL')

Current stock price in dollars. Fetches data when Cast runs (cached 5 min). Saved stock key awareness lets the assistant use an existing FMP key without asking again. Missing setup shows a data error. The Live Finance Data helper minimizes from its header and remembers your choice. Suggested code does not mean data has already been fetched.

Get current Apple stock price

price = stock('AAPL')

173.50 (live)

Swap AAPL for MSFT or TSLA — same formula, live price updates. Try stock('AAPL') / earnings('AAPL') for a P/E ratio.

earnings

Latest annual earnings per share (EPS).

earnings('SYMBOL')

Latest annual earnings per share (EPS). Fetched from income statement.

Calculate P/E ratio

pe = stock('AAPL') / earnings('AAPL')

28.5 (varies with market)

revenue

Latest annual revenue.

revenue('SYMBOL')

Latest annual revenue. Fetched from income statement.

Revenue per share

rev_per_share = revenue('AAPL') / market_cap('AAPL') * stock('AAPL')

Price-to-sales component

net_income

Latest annual net income (profit after all expenses and taxes).

net_income('SYMBOL')

Latest annual net income (profit after all expenses and taxes).

Profit margin

margin = net_income('AAPL') / revenue('AAPL')

0.25 (25% margin)

market_cap

Current market capitalization (total value of all shares).

market_cap('SYMBOL')

Current market capitalization (total value of all shares).

Compare company sizes

apple = market_cap('AAPL')
msft = market_cap('MSFT')

Market cap values for comparison

dividend

Annual dividend per share.

dividend('SYMBOL')

Annual dividend per share. Returns 0 if the company does not pay dividends.

Dividend yield

yield = dividend('AAPL') / stock('AAPL')

0.005 (0.5% yield)

stock_history

Array of N daily closing prices (most recent last).

stock_history('SYMBOL', days)

Array of N daily closing prices (most recent last). Use with stats functions for analysis.

30-day average and volatility

prices = stock_history('AAPL', 30) | hidden
avg_price = avg(prices)
volatility = cv(prices)

Average price and coefficient of variation

earnings_history

Array of 5 years of annual earnings per share (oldest first).

earnings_history('SYMBOL')

Array of 5 years of annual earnings per share (oldest first). Use with pearson() to test earnings growth trends.

Earnings growth trend

trend = pearson([1,2,3,4,5], earnings_history('AAPL'))

{ r: 0.95, significant: true } (strong uptrend)

revenue_history

Array of 5 years of annual revenue (oldest first).

revenue_history('SYMBOL')

Array of 5 years of annual revenue (oldest first). Use for trend analysis.

Revenue growth rate

revs = revenue_history('AAPL') | hidden
growth = revs[4] / revs[0] - 1

0.45 (45% growth over 5 years)

fetch

Fetch data from a custom API connector.

fetch('connector_name') or fetch('connector_name', 'param')

Fetch data from a custom API connector. Set up connectors in Settings > Live Data, or ask Cast to propose one.

Fetch Bitcoin price from a custom connector

btc = fetch('bitcoin_price')

63500 (current price from connector)

Fetch a specific cryptocurrency price

eth = fetch('crypto_price', 'ethereum')

3200 (Ethereum price from connector)

Accounting

Pull invoices, transactions, payments, and contacts from your connected accounting software.

Connection-dependent: accounting integrations are hidden on deployments where the provider is not configured.
invoices

Pull invoice data from connected accounting software (QuickBooks, Xero, FreshBooks).

invoices('status', 'field')

Pull invoice data from connected accounting software (QuickBooks, Xero, FreshBooks). Returns an array of field values filtered by status. Connect in Settings > Accounting.

Total accounts receivable

accounts_receivable = sum(invoices('outstanding', 'total_amount')) | result

$10,000 (sum of outstanding invoice amounts)

Count overdue invoices

overdue_invoices = count(invoices('overdue', 'total_amount')) | result

3 (number of overdue invoices)

Change 'outstanding' to 'overdue' — see late invoices only. Change 'total_amount' to 'balance_amount' for what is still owed.

contacts

Pull contact data from connected accounting software.

contacts('type', 'field')

Pull contact data from connected accounting software. Returns an array of field values filtered by type.

List customer names

customers = contacts('customer', 'name')

["Acme Corp", "Widget Inc", ...]

transactions

Pull transaction data from connected accounting software.

transactions('type', 'field')

Pull transaction data from connected accounting software. Returns an array of field values filtered by type.

Total expenses

total_expenses = sum(transactions('expense', 'total_amount')) | result

$45,000

Change 'expense' to 'revenue' — flip to income view. Wrap in avg() for the average transaction size.

payments

Pull payment data from connected accounting software.

payments('period', 'field')

Pull payment data from connected accounting software. Returns an array of field values filtered by period.

Payments received this year

payments_ytd = sum(payments('2026', 'amount')) | result

$120,000

Change '2026' to '2026-Q1' for a quarterly view. Try count() instead of sum() for number of payments.

accounts

Pull chart of accounts data from connected accounting software.

accounts('type', 'field')

Pull chart of accounts data from connected accounting software. Returns an array of field values filtered by type.

Asset account names

assets = accounts('asset', 'name')

["Cash", "Accounts Receivable", ...]

CAST