Circuitry for Jupyter Notebook Users
If you're coming from Jupyter notebooks, Circuitry offers a familiar yet more powerful approach to data analysis, visualization, and automation. This guide will help you transition from notebook-based workflows to visual workflow automation.
Already have notebooks? File → Import… converts a .ipynb file directly into a Circuitry CodeBook — see Import from Other Tools.
From Notebooks to Visual Workflows
Key Differences
| Jupyter Notebooks | Circuitry |
|---|---|
| Linear cell execution | Visual node-based workflows |
| Manual cell-by-cell runs | Automated end-to-end execution |
| Code in cells | Code in dedicated Code nodes |
| One kernel, one language per notebook | Per-cell language — Python, JavaScript, TypeScript, Go, Rust, C, C++ or Zig in the same document |
| Individual plots | Chart, DataViz, and StatPlot nodes |
| Manual data passing | Automatic data flow between nodes |
| Single environment | Multiple execution contexts |
One notebook, many languages
The difference worth dwelling on is the language row. A notebook is normally tied to one kernel, so the language you start in is the language you finish in. In a CodeBook every code cell picks its own, and cells pass data to each other regardless of what they're written in.
That changes who a notebook is for. If you work in Go, Rust, C, C++, Zig, JavaScript or TypeScript, you no longer have to translate your thinking into Python to get a notebook's benefits — you can explore, measure and narrate in the language you're actually productive in, and still drop into Python for the parts where its libraries are the reason you'd reach for it.
The trade-off is scope: Python cells share one persistent state, so variables carry from cell to cell. Cells in the other languages chain by data instead — each takes the previous cell's output as its input. See the CodeBook Tour for how that works in practice.
Core Concepts for Jupyter Users
1. Code Nodes Replace Cells
Instead of notebook cells, Circuitry uses Code nodes, and each one chooses its own language — Python, JavaScript, TypeScript, Go, Rust, C, C++ or Zig:
- Python and JavaScript run on the device you're working on, with nothing to install
- Pre-loaded Python libraries: NumPy, Pandas, Matplotlib, SciPy, Scikit-learn
- Additional packages via micropip
- Go, Rust, C, C++ and Zig are compiled the first time you run them and after each change — see Code nodes
- The previous step's value arrives as
input["value"], and a cell's last line is its value, as in a notebook — orreturnit explicitly
Example: Converting a Jupyter Cell
Jupyter Notebook:
import pandas as pd
import numpy as np
# Load data
data = pd.read_csv('data.csv')
# Process data
data['processed'] = data['value'] * 2
result = data.groupby('category').mean()
print(result)
Circuitry Code Node:
import pandas as pd
import numpy as np
# Input data flows from the previous node
data = pd.DataFrame(input["value"])
# Process data
data['processed'] = data['value'] * 2
# The last line is the value passed to the next node, as in a notebook
data.groupby('category').mean()
2. Visualization Nodes Replace Inline Plots
Circuitry provides three specialized visualization nodes that replace matplotlib/seaborn plots in notebooks:
Chart Node (Matplotlib-based)
- Best for: Traditional statistical charts (bar, line, scatter, histogram, pie)
- Configuration: JSON-based with chart type, data columns, styling
- Output: Base64 PNG images
DataViz Node (Plotly-based)
- Best for: Interactive visualizations (heatmaps, 3D plots, dashboards)
- Configuration: Plotly.js compatible settings
- Output: Interactive plots and static images
StatPlot Node (Seaborn-based)
- Best for: Statistical analysis plots (regression, distributions, correlations)
- Configuration: Seaborn style and palette options
- Output: Publication-ready statistical plots
3. Data Flow Instead of Variable Passing
In Jupyter, you manually pass variables between cells. In Circuitry, data flows automatically:
Jupyter approach:
# Cell 1
raw_data = load_data()
# Cell 2
cleaned_data = clean_data(raw_data)
# Cell 3
plot_data(cleaned_data)
Circuitry approach:
- Node 1: Load data → outputs
raw_data - Node 2: Clean data (receives
raw_dataasinput["value"]) → outputscleaned_data - Node 3: Plot data (receives
cleaned_dataasinput["value"])
In a CodeBook, Python cells also share one set of variables, so raw_data made in one cell is still there in the next — the notebook habit works as-is. Data flow is what carries a value into a cell written in another language, or into the next node of a workflow.
Migration Patterns
Pattern 1: Data Loading and Processing
Before (Jupyter):
# Cell 1: Load
import pandas as pd
df = pd.read_csv('sales.csv')
# Cell 2: Clean
df = df.dropna()
df['date'] = pd.to_datetime(df['date'])
# Cell 3: Analyze
monthly_sales = df.groupby(df['date'].dt.month).sum()
After (Circuitry):
- HTTP Request Node: Fetch CSV data from API
- Code Node (Data Cleaning):
import pandas as pd df = pd.DataFrame(input["value"]) df = df.dropna() df['date'] = pd.to_datetime(df['date']) df - Code Node (Analysis):
import pandas as pd df = pd.DataFrame(input["value"]) df.groupby(df['date'].dt.month).sum()
Pattern 2: Visualization Workflows
Before (Jupyter):
# Analysis cell
correlation_matrix = df.corr()
# Plotting cell
import matplotlib.pyplot as plt
import seaborn as sns
plt.figure(figsize=(10, 8))
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm')
plt.title('Feature Correlations')
plt.show()
After (Circuitry):
-
Code Node (Analysis):
import pandas as pd df = pd.DataFrame(input["value"]) df.corr() -
StatPlot Node Configuration:
{ "plotType": "heatmap", "title": "Feature Correlations", "width": 10, "height": 8, "style": "whitegrid", "customCode": "sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm')" }
Pattern 3: Machine Learning Pipelines
Before (Jupyter):
# Data prep
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# Training
model = RandomForestClassifier()
model.fit(X_train, y_train)
# Evaluation
predictions = model.predict(X_test)
accuracy = accuracy_score(y_test, predictions)
print(f"Accuracy: {accuracy}")
After (Circuitry):
-
Code Node (Data Split):
from sklearn.model_selection import train_test_split import pandas as pd df = pd.DataFrame(input["value"]) X = df.drop('target', axis=1) y = df['target'] X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) { 'X_train': X_train.to_dict('records'), 'X_test': X_test.to_dict('records'), 'y_train': y_train.tolist(), 'y_test': y_test.tolist() } -
Code Node (Training):
from sklearn.ensemble import RandomForestClassifier import pandas as pd data = input["value"] X_train = pd.DataFrame(data['X_train']) y_train = data['y_train'] model = RandomForestClassifier() model.fit(X_train, y_train) # In practice, you'd serialize the model { 'model_trained': True, 'X_test': data['X_test'], 'y_test': data['y_test'] } -
Code Node (Evaluation):
# Evaluate model and create results accuracy = 0.95 # placeholder {'accuracy': accuracy}
Advanced Features for Data Scientists
1. Template Variables
Use {{variable}} syntax to make workflows dynamic:
# Code node with template variables
model_type = "{{model_type}}" # Set via environment variables
test_size = {{test_size}} # Numeric templates
if model_type == "random_forest":
from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier()
2. Conditional Workflows
Use Condition nodes for branching logic:
- If data quality score > 0.8 → Continue with complex model
- Else → Use simple baseline model
3. Parallel Processing
Use Fork/Join nodes for parallel execution:
- Fork: Split data for multiple model training
- Join: Combine results for ensemble methods
4. Automation
Unlike notebooks, Circuitry workflows can be:
- Triggered by webhooks for real-time data processing
- Scheduled for batch processing
- Chained with other workflows
- Deployed as APIs
Best Practices for Migration
1. Start Small
- Begin with simple data loading + visualization workflows
- Gradually add complexity with conditions and loops
- Practice data flow concepts before complex logic
2. Leverage Code Nodes
- Use Code nodes for complex Python logic
- Keep visualization in Chart/DataViz/StatPlot nodes
- Break large notebook cells into focused nodes
3. Design for Reusability
- Create modular workflows that can be called from other workflows
- Use template variables for configurable parameters
- Document node purposes with clear names
4. Test Data Flow
- Use the execution output panel to verify data passing
- Add debug Code nodes to inspect intermediate results
- Leverage the visual workflow graph to understand dependencies
5. Optimize for Performance
- Use Image nodes to display static plots without re-execution
- Cache expensive computations in separate nodes
- Consider breaking long-running processes into smaller nodes
Common Gotchas
1. What a Cell Passes On
A cell's value is its last line when that line is an expression, exactly as in Jupyter — or whatever you return at the top level. print() shows output but is not passed on. Values travel between cells and nodes as data, so a DataFrame is passed on as a list of rows (one record per row), and the next cell reads it with input["value"]:
# Cell 1
df = load_data()
df # ✅ the value: passed on as a list of records
# Cell 2
rows = input["value"]
pd.DataFrame(rows)
One thing to avoid: a dictionary with its own "value" key is taken for the value itself and unwrapped. Pick another name for that key.
2. Plots
plt.show() shows every open figure in the cell's output, as in a notebook. So does ending a cell on a plotting call (plt.plot(x, y), df.plot()) or on the figure itself. %matplotlib inline is not needed; the line is accepted and does nothing.
import matplotlib.pyplot as plt
plt.plot([1, 4, 9, 16])
plt.title("Squares")
plt.show()
To hand the picture to the next cell instead (an Image cell directly below draws it), save the figure to PNG and end the cell with it:
import io, base64
import matplotlib.pyplot as plt
df.plot()
buf = io.BytesIO()
plt.savefig(buf, format="png", bbox_inches="tight")
plt.close()
"data:image/png;base64," + base64.b64encode(buf.getvalue()).decode()
Matplotlib — Pictures from Numbers explains the frame; the CodeBook tutorial uses it end to end.
3. Library Imports
Install a package the way you would in a notebook. !pip install and %pip install work in every interactive run: in the browser, on a computer with Circuit, and on iPhone and iPad:
!pip install seaborn
import seaborn as sns
Each package installs the first time and is already there on later runs. On iPhone and iPad, a package that needs compiled parts runs on the page's Python automatically. To show charts and tables from a Python file (fig.show(), plt.show(), display(df)), see Charts and Tables. More on installing: Installing Python Packages.
4. File Handling
Browser-based execution has different file access patterns:
# Instead of local file reading
data = pd.read_csv('local_file.csv') # ❌ May not work
# Use HTTP requests or pass data via workflow
data = pd.DataFrame(input["value"]) # ✅ Works
5. display() and IPython
The line most notebooks start with works:
from IPython.display import display, HTML, Markdown, Math, Image, clear_output
display(df) # a table
display(Markdown("## Results")) # formatted text
display(Math(r"e^{i\pi} + 1 = 0")) # typeset maths
display(Image(filename="chart.png", width=400))
What is displayed appears in the cell's output, in order with what the cell prints. display() and get_ipython() also work without the import.
- The last line is shown too. A DataFrame, a figure, a sympy expression or
HTML(...)on a cell's last line appears as its table, picture or formula, and is still the value the cell passes on. - Available:
display,update_displayanddisplay_id,clear_output,HTML,Markdown,Math,Latex,Image,SVG,JSON,Audio,Video,IFrame,YouTubeVideo,Code,Pretty,ProgressBar. - HTML runs sealed off. Markup a program displays is shown in a frame of its own that cannot reach your documents or your account. Some sites refuse to appear inside an
IFrame; the link under it opens the page. - Not available:
Javascript(...)does not run (its place says so), and there is noipywidgets. - Magics:
%matplotlib inline,%load_ext,%autoreloadand%configare accepted and do nothing;%timeand%timeitrun their statement once. Shell lines (!ls),%%bashand other magics do not run: remove them or write the Python for them.!pip installis the exception, see above.
Resources for Learning
- Your First CodeBook - Every kind of cell, step by step, in Python
- Code Node Guide - Detailed Code node and Python execution documentation
- Template Variables - Dynamic workflow configuration
- Workflow Patterns - Common workflow designs
Example Notebooks to Workflows
Data Analysis Workflow
Goal: Analyze sales data and create dashboard
Jupyter: 15+ cells with manual execution Circuitry: 6 connected nodes with automatic execution
- Data loading → Cleaning → Analysis → Multiple visualizations → Dashboard
ML Model Training
Goal: Train and evaluate classification model
Jupyter: Manual feature engineering, training, evaluation Circuitry: Automated pipeline with parallel model comparison
- Data prep → Feature engineering → Fork (multiple models) → Join → Best model selection
The transition from Jupyter notebooks to Circuitry workflows opens up new possibilities for automation, collaboration, and production deployment of your data science work.