diff --git a/benches/cases/create-computed/index.html b/benches/cases/create-computed/index.html
new file mode 100644
index 000000000..111782cde
--- /dev/null
+++ b/benches/cases/create-computed/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
Benchmarks
+
+ This is a list of benchmarks we use to measure the performance of
+ singals with.
+
+
View results on the results page.
+
Cases
+
+
+
+
diff --git a/benches/package.json b/benches/package.json
new file mode 100644
index 000000000..27a35cacb
--- /dev/null
+++ b/benches/package.json
@@ -0,0 +1,21 @@
+{
+ "name": "demo",
+ "private": true,
+ "scripts": {
+ "start": "vite",
+ "build": "vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "preact": "10.9.0",
+ "@preact/signals-core": "workspace:../packages/core",
+ "@preact/signals": "workspace:../packages/preact"
+ },
+ "devDependencies": {
+ "@types/connect": "^3.4.35",
+ "@types/express": "^4.17.14",
+ "express": "^4.18.1",
+ "tiny-glob": "^0.2.9",
+ "vite": "^3.0.7"
+ }
+}
diff --git a/benches/public/favicon.ico b/benches/public/favicon.ico
new file mode 100644
index 000000000..4b9e9b56e
Binary files /dev/null and b/benches/public/favicon.ico differ
diff --git a/benches/results.html b/benches/results.html
new file mode 100644
index 000000000..d34ca7de1
--- /dev/null
+++ b/benches/results.html
@@ -0,0 +1,32 @@
+
+
+
+
+
Results
+
+ The numbers will be updated whenever you run a benchmark and refresh
+ this page.
+
+
+
+
+ Benchmark Name |
+ Time Range |
+ Memory |
+
+
+
+ {%ITEMS%}
+
+
+
+
+
diff --git a/benches/style.css b/benches/style.css
new file mode 100644
index 000000000..38ef121a7
--- /dev/null
+++ b/benches/style.css
@@ -0,0 +1,58 @@
+body {
+ font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif,
+ "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
+}
+
+:root {
+ --primary: #673ab8;
+ --even: #f3f3f3;
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --even: #242424;
+ }
+}
+
+.page {
+ margin: 0 auto;
+ max-width: 40rem;
+ padding: 2rem;
+}
+
+table {
+ border-collapse: collapse;
+ margin: 25px 0;
+ font-size: 0.9em;
+ font-family: sans-serif;
+ min-width: 400px;
+ box-shadow: 0 0 20px rgba(0, 0, 0, 0.15);
+}
+
+table thead tr {
+ background-color: var(--primary);
+ color: #ffffff;
+ text-align: left;
+}
+
+table th,
+table td {
+ padding: 12px 15px;
+}
+
+table tbody tr {
+ border-bottom: 1px solid #dddddd;
+}
+
+table tbody tr:nth-of-type(even) {
+ background-color: var(--even);
+}
+
+table tbody tr:last-of-type {
+ border-bottom: 2px solid var(--primary);
+}
+
+table tbody tr.active-row {
+ font-weight: bold;
+ color: var(--primary);
+}
diff --git a/benches/tsconfig.json b/benches/tsconfig.json
new file mode 100644
index 000000000..e7d301c81
--- /dev/null
+++ b/benches/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../tsconfig.json",
+ "compilerOptions": {
+ "jsx": "react-jsx",
+ "jsxImportSource": "preact",
+ "module": "esnext"
+ }
+}
diff --git a/benches/vite.config.ts b/benches/vite.config.ts
new file mode 100644
index 000000000..b06411fb6
--- /dev/null
+++ b/benches/vite.config.ts
@@ -0,0 +1,177 @@
+import { defineConfig, Plugin } from "vite";
+import { resolve, posix } from "path";
+import fs from "fs";
+import { NextHandleFunction } from "connect";
+import * as express from "express";
+
+// Automatically set up aliases for monorepo packages.
+// Uses built packages in prod, "source" field in dev.
+function packages(prod: boolean) {
+ const alias: Record