diff --git a/examples/cancel/README.md b/examples/cancel/README.md
new file mode 100644
index 000000000..33e5422d9
--- /dev/null
+++ b/examples/cancel/README.md
@@ -0,0 +1,5 @@
+# Cancel Example
+
+Demonstrates how to cancel an inflight `upscale` request.
+
+[See this live](https://githubbox.com/thekevinscott/upscalerjs/tree/main/examples/cancel).
diff --git a/examples/cancel/flower.png b/examples/cancel/flower.png
new file mode 100644
index 000000000..5c8dcab02
Binary files /dev/null and b/examples/cancel/flower.png differ
diff --git a/examples/cancel/index.html b/examples/cancel/index.html
new file mode 100644
index 000000000..a46764147
--- /dev/null
+++ b/examples/cancel/index.html
@@ -0,0 +1,65 @@
+
+
+ Cancel Example | Upscaler.JS
+
+
+
+ Cancel Example
+ An example demonstrating how to cancel an inflight request of the upscale method.
+ Click the upscale button (it is purposefully slow) to upscale the photo, and then click cancel to cancel it.
+
+
+
+
+
+ Original | Upscaled |
+
+
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+
+
diff --git a/examples/cancel/index.js b/examples/cancel/index.js
new file mode 100644
index 000000000..9a3568f79
--- /dev/null
+++ b/examples/cancel/index.js
@@ -0,0 +1,39 @@
+import Upscaler from 'upscaler';
+import img from './flower.png';
+const target = document.getElementById('target');
+const upscale = document.getElementById('upscale');
+const cancel = document.getElementById('cancel');
+const info = document.getElementById('info');
+
+let abortController = new AbortController();
+const upscaler = new Upscaler();
+upscale.onclick = () => {
+ info.innerText = 'Upscaling...';
+ const start = new Date().getTime();
+ let rate = 0;
+ upscaler.upscale(img, {
+ patchSize: 16,
+ padding: 4,
+ signal: abortController.signal,
+ progress: (inProgressRate) => {
+ rate = inProgressRate.toFixed(2);
+ info.innerText = `Upscaling (${rate}%)...`;
+ },
+ }).then((upscaledImgSrc) => {
+ const img = document.createElement('img');
+ img.src = upscaledImgSrc;
+ target.innerHTML = '';
+ target.appendChild(img);
+ const ms = new Date().getTime() - start;
+ info.innerText = `Upscaled in ${ms} ms`;
+ }).catch(err => {
+ console.log('The AbortError:', err);
+ info.innerText = `Canceled at ${rate}%`;
+ });
+};
+
+cancel.onclick = () => {
+ console.log('canceled');
+ abortController.abort();
+ abortController = new AbortController();
+}
diff --git a/examples/cancel/package.json b/examples/cancel/package.json
new file mode 100644
index 000000000..a31136a6e
--- /dev/null
+++ b/examples/cancel/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "cancel",
+ "version": "0.11.0",
+ "description": "Demonstrates how to cancel an inflight upscale request.",
+ "main": "index.js",
+ "scripts": {
+ "copy": "rm -rf dist && mkdir dist && cp index.html dist && cp flower.png dist",
+ "start": "yarn copy && esbuild index.js --servedir=dist --outdir=dist --bundle --loader:.png=dataurl"
+ },
+ "author": "Kevin Scott",
+ "license": "MIT",
+ "dependencies": {
+ "@tensorflow/tfjs": "^3.13.0",
+ "upscaler": "0.11.0"
+ },
+ "browserslist": {
+ "production": [
+ ">0.2%",
+ "not dead",
+ "not op_mini all"
+ ],
+ "development": [
+ "last 1 chrome version",
+ "last 1 firefox version",
+ "last 1 safari version"
+ ]
+ }
+}
diff --git a/examples/cancel/sandbox.config.json b/examples/cancel/sandbox.config.json
new file mode 100644
index 000000000..39b4b0bdc
--- /dev/null
+++ b/examples/cancel/sandbox.config.json
@@ -0,0 +1,5 @@
+{
+ "infiniteLoopProtection": false,
+ "hardReloadOnChange": true,
+ "view": "browser"
+}
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index 0b0b6b0d6..d1a8c71be 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4365,14 +4365,6 @@
resolved "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.0.0.tgz"
integrity sha512-NZwaaynfs1oIoLAV1vg18e7QMVDvw+6SQrdJc8w3BwUaoroVSf6EBj/Sk4PBWGxsq0dzhA2drbsuMC1/6C6KgQ==
-"@types/inquirer@^8.2.0":
- version "8.2.0"
- resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.0.tgz#b9566d048f5ff65159f2ed97aff45fe0f00b35ec"
- integrity sha512-BNoMetRf3gmkpAlV5we+kxyZTle7YibdOntIZbU5pyIfMdcwy784KfeZDAcuyMznkh5OLa17RVXZOGA5LTlkgQ==
- dependencies:
- "@types/through" "*"
- rxjs "^7.2.0"
-
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
version "2.0.3"
resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz"
@@ -4560,13 +4552,6 @@
resolved "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.8.tgz"
integrity sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ==
-"@types/through@*":
- version "0.0.30"
- resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.30.tgz#e0e42ce77e897bd6aead6f6ea62aeb135b8a3895"
- integrity sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==
- dependencies:
- "@types/node" "*"
-
"@types/tough-cookie@*":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.1.tgz#8f80dd965ad81f3e1bc26d6f5c727e132721ff40"