LucidPlexus
Sign inCreate account

The Workbench, documented.

Everything the code environment does, in one page: the engines, the syntax, working examples, and the path from raw dataset to published work. Every snippet below runs as-is in a Workbench block.

๐Ÿงช The environment

What actually runs when you press Run.

The Workbench is a notebook of blocks โ€” note, SQL, and Python โ€” and the whole engine runs inside your browser tab. Nothing is installed on your machine and no code or data is sent to a server to execute:

Block typeEngineWhat that means
SQLDuckDB (WebAssembly)A real analytical database in the tab. Loads with the page; queries run instantly on the attached datasets.
PythonPyodide โ€” CPython in WebAssemblyReal Python with pandas, numpy, and matplotlib. Downloads ~15 MB the first time you run a Python block, then it is cached.
Noteโ€”Prose. Notes become the README of your published work.
๐Ÿ”’ Privacy consequence: your queries and your work never leave the browser. The server stores your blocks and their saved outputs โ€” the execution itself is entirely yours.

Deliberate limits (they are features, not bugs): Python has no network access, no pip install, no input(), and no threads; memory is what the browser tab allows. Everything a project needs ships with the runtime.

๐Ÿ—‚๏ธ Getting data in

Upload โ†’ attach โ†’ alias. The alias is the table name.

Data enters the platform once, at the top, and flows down to every member:

  1. Upload (curators): an admin uploads a CSV in the dataset manager. Rows and columns are scanned automatically and the dataset appears in the public library.
  2. Attach (brief authors): a project brief attaches one or more datasets, each under an alias. Aliases are validated because they become SQL table names โ€” budget works, Budget Data! is rejected.
  3. Use (you): open the Workbench on a claimed brief and every attached dataset is already loaded as a table under its alias. The left rail lists each one with a โœ“ and its row count.
-- The aliases in the left rail ARE your tables. First look:
SELECT * FROM budget LIMIT 5;

Members do not upload their own files in the current version โ€” every project runs on curated data, which is what makes review and auto-checks possible. Have a dataset worth adding? Mail it to the address in the dataset library sidebar.

๐Ÿค SQL guide

The dialect is DuckDB. If you learned MySQL or Postgres, 95% carries over โ€” the differences are listed at the end.

Selecting and filtering

SELECT category, budgeted_amount, actual_amount
FROM   budget
WHERE  actual_amount > budgeted_amount   -- conditions can compare columns
ORDER BY actual_amount - budgeted_amount DESC
LIMIT 5;

Aggregating and grouping

SELECT category,
       COUNT(*)                                   AS line_items,
       SUM(actual_amount)                         AS spent,
       SUM(actual_amount) - SUM(budgeted_amount)  AS variance
FROM   budget
GROUP BY category
HAVING SUM(actual_amount) > 1000000   -- HAVING filters groups; WHERE filters rows
ORDER BY variance;
โš ๏ธ WHERE cannot see aggregates โ€” it runs before the groups exist. Filtering on an AVG or SUM belongs in HAVING. Getting this wrong produces a Binder Error, and that error is correct.

Dates

SELECT strftime(due_date, '%Y')                        AS year,
       date_diff('day', due_date, DATE '2027-01-01')   AS days_overdue
FROM   receivables
WHERE  due_date BETWEEN DATE '2022-01-01' AND DATE '2022-12-31';

Write date literals as DATE 'YYYY-MM-DD'. date_diff('day', a, b) counts from a to b; strftime(date, format) formats it.

Banding with CASE

SELECT CASE WHEN receivable_amount >= 30000 THEN 'large'
            WHEN receivable_amount >= 10000 THEN 'medium'
            ELSE 'small'
       END AS size_band,
       COUNT(*) AS invoices
FROM   receivables
GROUP BY size_band;   -- grouping by the alias works

Joins

SELECT b.category, d.dept_head, SUM(b.actual_amount) AS spent
FROM   budget b
JOIN   departments d ON b.category = d.category
GROUP BY b.category, d.dept_head
ORDER BY spent DESC;
-- LEFT JOIN keeps rows with no match (departments that spent nothing)

Subqueries and CTEs

WITH dept AS (
  SELECT category, SUM(actual_amount) AS spent
  FROM   budget
  GROUP BY category
)
SELECT category, spent,
       ROUND(spent * 100.0 / (SELECT SUM(spent) FROM dept), 1) AS pct
FROM   dept
ORDER BY spent DESC;

Views โ€” saving a query under a name

CREATE OR REPLACE VIEW clean_budget AS
SELECT * FROM budget WHERE actual_amount >= 0;

SELECT SUM(actual_amount) FROM clean_budget;  -- reads through the view
๐Ÿ” Views live in the browser session. After a page refresh the database is rebuilt from the datasets, so run the blocks that create your views first โ€” or simply press โ–ถโ–ถ Run all, which replays everything in order.

Coming from MySQL? The differences that bite

HabitHere
`backtick` quotingUse "double quotes" for odd identifiers (rarely needed โ€” aliases are validated).
LIMIT 10, 5LIMIT 5 OFFSET 10
DATE_SUB / DATE_ADDdate_diff(โ€ฆ), d + INTERVAL 7 DAY
IFNULL(x, y)COALESCE(x, y) (works everywhere)
Integer division 3/2 = 13/2 = 1.5 โ€” use // for integer division

๐Ÿ Python guide

Real Python in the tab, with a tiny bridge to your data called lp.

CallReturns
lp.datasets()The list of attached dataset aliases.
lp.table('budget')That dataset as a pandas DataFrame.
await lp.sql("SELECT โ€ฆ")Any SQL โ€” including your views โ€” run on DuckDB, back as a DataFrame. Note the await.
# Cross-check a SQL number independently in pandas
df = await lp.sql("SELECT * FROM invoices")
overdue = df[df.is_overdue]

print("overdue invoices:", len(overdue))
print(f"overdue amount: {overdue.receivable_amount.sum():,}")

Output rules, in order: everything you print appears as stdout; a DataFrame that is the last expression of the block renders as a table (first 50 rows, with the true total shown); matplotlib figures appear as images โ€” up to two plots per run.

import matplotlib.pyplot as plt

cash = await lp.sql("""
  SELECT cash_type, SUM(inflow) - SUM(outflow) AS net_cash
  FROM cash_flow GROUP BY cash_type
""")
plt.bar(cash.cash_type, cash.net_cash)
plt.title("Net cash by activity")
plt.show()

Available out of the box: pandas, numpy, matplotlib, and the Python standard library. Not available, on purpose: network requests, pip installs, file system, input(), threads. If a script needs the internet, it belongs on your own machine โ€” the Workbench is for the analysis.

โ–ถ๏ธ Running code & the pipeline

One block, the whole notebook, and the badge that proves it.

โ–ถ Run โ€” executes one block (Ctrl/โŒ˜ + Enter inside the editor does the same). The result is saved with the block, so your outputs are still there tomorrow.

โ–ถโ–ถ Run all โ€” executes every included SQL and Python block top to bottom and stops at the first error. If everything passes, the work earns a โ›“ Reproducible stamp, shown on the published page โ€” proof the notebook runs clean start to finish, not just in the order you happened to click.

๐ŸŽฏ Checks โ€” tasks can carry an auto-check: run it and your current result is compared to the expected answer, pass or fail with the actual value shown. Reviewers see the same check summary.

Errors โ€” appear under the block, verbatim from the engine. Read them; they usually name the problem (an aggregate in WHERE, a missing table, a typo in a column). An error in a Run all halts the pipeline at that block so you always know where it broke.

๐Ÿš€ From Workbench to published work

The whole loop, end to end.

  1. Claim โ€” pick a brief; the Workbench opens with its datasets loaded and its tasks in the ๐Ÿ“‹ drawer.
  2. Work โ€” add note, SQL, and Python blocks; reorder freely; toggle any block out of the final piece. Included blocks become the README.
  3. Verify โ€” โ–ถโ–ถ Run all until green, run the ๐ŸŽฏ checks, cross-check a key number two ways.
  4. Submit โ€” your work enters the review queue. A human reviews it; changes may be requested with notes.
  5. Published โ€” the repo goes public on your portfolio, readable logged-out, with block-level comments โ€” and XP and coins land in your account.

๐Ÿ’ฌ Questions & answers

The ones every new member asks.

Why is my first Python run slow?
The Python runtime (~15 MB) downloads on your first โ–ถ Run of a Python block, then it is cached by the browser. SQL never has this wait โ€” DuckDB loads with the page. Later runs are instant.
Do my views and results survive a refresh?
Saved block outputs survive โ€” they are stored with your work. The in-browser database is rebuilt on refresh, so views (CREATE OR REPLACE VIEW) exist again only after the block that creates them runs. Habit worth forming: after a refresh, press โ–ถโ–ถ Run all once.
My query worked in MySQL but errors here. Why?
The Workbench speaks DuckDB. The usual culprits: backtick quoting, the LIMIT x,y form, and MySQL date functions. The conversion table in the SQL guide above covers all of them โ€” the fix is typically one line.
Where does my code actually run? Who can see my data?
In your browser tab, entirely. Datasets are fetched into the tab and queried there; code is never executed on our servers. What we store is your notebook โ€” the blocks and the outputs you saved with them.
Can I upload my own dataset?
Not yet โ€” projects run on curated datasets so that review and auto-checks stay meaningful. You can propose one: mail it via the link in the dataset library sidebar, and credited curated additions do happen.
Can I use requests / install a package with pip?
No. The Python sandbox has no network and no installer, by design โ€” it keeps every published notebook runnable by every reader forever, with nothing to break or leak. pandas, numpy, and matplotlib cover the analysis work the platform teaches.
What does the โ›“ Reproducible badge mean exactly?
That the last โ–ถโ–ถ Run all executed every included code block, top to bottom, with zero errors and nothing skipped. It is stamped automatically โ€” there is no way to hand-award it โ€” which is why reviewers and readers trust it.
How are the ๐ŸŽฏ checks graded?
A check runs a query against your current session and compares the result to the value the brief author defined โ€” an exact number or a minimum. You see pass or fail plus your actual value, and the reviewer sees the same summary. No AI grading is involved.
Why do I only see some rows of my result?
Result tables display the first rows and tell you the true total (\"first 5 shown ยท of 1,000\"). The full result exists in the engine โ€” add ORDER BY and LIMIT to pull exactly the slice you want to look at.
Something says the SQL engine failed to load. What do I do?
The left rail names the problem when an engine cannot start โ€” usually a network block between your browser and the script CDN, or a brief whose dataset has no file attached. Try a refresh first; if it persists, the message on the page says exactly what to report to support.
Who can see my draft before I publish?
Only you โ€” and reviewers, after you submit. Published repos are public by design; drafts never are.
Is there AI grading or an AI assistant?
Review is human and checks are deterministic. An AI assistant panel exists in the product design but is switched off until it works properly โ€” pages never pretend otherwise. When it ships, this page will document it.