diff --git a/docz/docs/03-demos/09-cloud/11-aws.md b/docz/docs/03-demos/09-cloud/11-aws.md
index 01bb6be..647b2b8 100644
--- a/docz/docs/03-demos/09-cloud/11-aws.md
+++ b/docz/docs/03-demos/09-cloud/11-aws.md
@@ -354,8 +354,8 @@ required in NodeJS scripts.
### Connecting to S3
-The `AWS` module includes a function `S3` that performs the connection. Access
-keys for an IAM user[^9] must be used:
+The `aws-sdk` module includes a function `S3` that performs the connection.
+Access keys for an IAM user[^9] must be used:
```js
/* credentials */
diff --git a/docz/docs/03-demos/32-extensions/09-mathematica.md b/docz/docs/03-demos/32-extensions/09-mathematica.md
index ce559aa..a01f484 100644
--- a/docz/docs/03-demos/32-extensions/09-mathematica.md
+++ b/docz/docs/03-demos/32-extensions/09-mathematica.md
@@ -21,7 +21,7 @@ data from opaque spreadsheets and parse the data from Mathematica.
:::note
-This demo was last tested in 2023 August 21 in Mathematica 13.2.1.
+This demo was last tested by SheetJS users on 2023 August 21 in Mathematica 13.
:::
diff --git a/docz/docs/03-demos/32-extensions/11-matlab.md b/docz/docs/03-demos/32-extensions/11-matlab.md
index e730739..8acba17 100644
--- a/docz/docs/03-demos/32-extensions/11-matlab.md
+++ b/docz/docs/03-demos/32-extensions/11-matlab.md
@@ -21,7 +21,7 @@ spreadsheets into simple XLSX files for MATLAB.
:::note
-This demo was last tested in 2023 September 12 in MATLAB R2023a.
+This demo was last tested by SheetJS users on 2023 September 12 in MATLAB R2023a.
:::
diff --git a/docz/docs/03-demos/32-extensions/12-maple.md b/docz/docs/03-demos/32-extensions/12-maple.md
new file mode 100644
index 0000000..f63f2f7
--- /dev/null
+++ b/docz/docs/03-demos/32-extensions/12-maple.md
@@ -0,0 +1,209 @@
+---
+title: Modern Spreadsheets in Maple
+sidebar_label: Maple
+pagination_prev: demos/cloud/index
+pagination_next: demos/bigdata/index
+---
+
+import current from '/version.js';
+import CodeBlock from '@theme/CodeBlock';
+
+[Maple](https://www.maplesoft.com/products/Maple/) is a numeric computing
+platform. It offers a robust C-based extension system.
+
+[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
+data from spreadsheets.
+
+This demo uses SheetJS to pull data from a spreadsheet for further analysis
+within Maple. We'll create a Maple native extension that loads the
+[Duktape](/docs/demos/engines/duktape) JavaScript engine and uses the SheetJS
+library to read data from spreadsheets and converts to a Maple-friendly format.
+
+```mermaid
+flowchart LR
+ ofile[(workbook\nXLSB file)]
+ nfile[(clean file\nXLSX)]
+ data[[Maple\nTable]]
+ ofile --> |Maple Extension\nSheetJS + Duktape| nfile
+ nfile --> |ExcelTools\nImport|data
+```
+
+:::note
+
+This demo was last tested by SheetJS users on 2023 October 3 in Maple 2023.
+
+:::
+
+:::info pass
+
+Maple has limited support for processing spreadsheets through the `ExcelTools`
+package[^1]. At the time of writing, it lacked support for XLSB, NUMBERS, and
+other common spreadsheet formats.
+
+SheetJS libraries help fill the gap by normalizing spreadsheets to a form that
+Maple can understand.
+
+:::
+
+## Integration Details
+
+The current recommendation involves a native plugin that reads arbitrary files
+and generates clean XLSX files that Maple can import.
+
+The extension function ultimately pairs the SheetJS `read`[^2] and `write`[^3]
+methods to read data from the old file and write a new file:
+
+```js
+var wb = XLSX.read(original_file_data, {type: "buffer"});
+var new_file_data = XLSX.write(wb, {type: "array", bookType: "xlsx"});
+```
+
+The extension function will receive a file name and perform the following steps:
+
+```mermaid
+flowchart LR
+ ofile{{File\nName}}
+ subgraph JS Operations
+ ojbuf[(Buffer\nFile Bytes)]
+ wb(((SheetJS\nWorkbook)))
+ njbuf[(Buffer\nXLSX bytes)]
+ end
+ obuf[(File\nbytes)]
+ nbuf[(New file\nbytes)]
+ nfile[(XLSX\nFile)]
+ ofile --> |C\nRead File| obuf
+ obuf --> |Duktape\nBuffer Ops| ojbuf
+ ojbuf --> |SheetJS\n`read`| wb
+ wb --> |SheetJS\n`write`| njbuf
+ njbuf --> |Duktape\nBuffer Ops| nbuf
+ nbuf --> |C\nWrite File| nfile
+```
+
+### C Extensions
+
+Maple C extensions are shared libraries or DLLs that use special Maple methods
+for parsing arguments and returning values.
+
+To simplify the flow, the new function will take one argument (the original file
+name) and return one value (the new file name).
+
+The official documentation has a comprehensive list[^4] of methods. For this
+demo, the following methods are used:
+
+- `MapleNumArgs` and `IsMapleString` are used in argument validation. The demo
+function will raise a Maple exception if no file name is specified.
+
+- `MapleRaiseError` and `MapleRaiseError2` programmatically raise errors.
+
+- `MapleToString` and `ToMapleString` convert between Maple and C strings.
+
+### Duktape JS Engine
+
+This demo uses the [Duktape JavaScript engine](/docs/demos/engines/duktape). The
+SheetJS + Duktape demo covers engine integration details in more detail.
+
+The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone)
+can be loaded in Duktape by reading the source from the filesystem.
+
+## Complete Demo
+
+:::info pass
+
+This demo was tested in Windows x64. The path names and build commands will
+differ in other platforms and operating systems.
+
+:::
+
+The [`sheetjs-maple.c`](pathname:///maple/sheetjs-maple.c) extension exports the
+`SheetToXLSX` Maple method. It takes a file name argument, parses the specified
+file, exports data to `sheetjsw.xlsx` and returns the string `"sheetjsw.xlsx"`.
+
+This can be chained with `Import` from `ExcelTools`:
+
+```maple
+with(ExcelTools);
+Import(SheetToXLSX("pres.numbers"))
+```
+
+0) Ensure "Windows Subsystem for Linux" (WSL) and Visual Studio are installed.
+
+1) Open a new "x64 Native Tools Command Prompt" window and create a project
+folder `c:\sheetjs-maple`:
+
+```powershell
+cd c:\
+mkdir sheetjs-maple
+cd sheetjs-maple
+```
+
+2) Copy the headers and `lib` files from the Maple folder to the project folder.
+For example, using Maple 2023 on Windows x64:
+
+```powershell
+copy "C:\Program Files\Maple 2023\extern\include\"*.h .
+copy "c:\Program Files\Maple 2023\bin.x86_64_WINDOWS"\*.lib .
+```
+
+3) Run `bash` to enter WSL
+
+4) Within WSL, install Duktape:
+
+```bash
+curl -LO https://duktape.org/duktape-2.7.0.tar.xz
+tar -xJf duktape-2.7.0.tar.xz
+mv duktape-2.7.0/src/*.{c,h} .
+```
+
+5) Still within WSL, download SheetJS scripts and the test file.
+
+{`\
+curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js
+curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
+curl -LO https://sheetjs.com/pres.numbers`}
+
+
+6) Still within WSL, download the extension C code
+
+```bash
+curl -LO https://docs.sheetjs.com/maple/sheetjs-maple.c
+```
+
+7) Exit WSL by running `exit`. The window will return to the command prompt.
+
+8) Build the extension DLL:
+
+```powershell
+cl -Gz sheetjs-maple.c duktape.c /EHsc -link -dll -out:sheetjs-maple.dll maplec.lib
+```
+
+9) Close and re-open Maple, then create a new Maple Worksheet or Document
+
+10) Run the following command in Maple to change the working directory:
+
+```maple
+currentdir("c:\\sheetjs-maple");
+```
+
+11) Load the `SheetToXLSX` method from the extension:
+
+```maple
+with(ExternalCalling):
+dll:=ExternalLibraryName("sheetjs-maple"):
+SheetToXLSX:=DefineExternal("SheetToXLSX",dll):
+```
+
+12) Read the `pres.numbers` test file:
+
+```maple
+with(ExcelTools);
+Import(SheetToXLSX("pres.numbers"))
+```
+
+The result will show the data from `pres.numbers`
+
+![Maple Screenshot](pathname:///maple/maple.png)
+
+[^1]: See ["ExcelTools"](https://www.maplesoft.com/support/help/Maple/view.aspx?path=ExcelTools) in the Maple documentation.
+[^2]: See [`read` in "Reading Files"](/docs/api/parse-options)
+[^3]: See [`write` in "Writing Files"](/docs/api/write-options)
+[^4]: See ["C OpenMaple and ExternalCalling Application Program Interface (API)"](https://www.maplesoft.com/support/help/maple/view.aspx?path=OpenMaple%2FC%2FAPI) in the Maple documentation.
\ No newline at end of file
diff --git a/docz/static/maple/maple.png b/docz/static/maple/maple.png
new file mode 100644
index 0000000..3234b65
Binary files /dev/null and b/docz/static/maple/maple.png differ
diff --git a/docz/static/maple/sheetjs-maple.c b/docz/static/maple/sheetjs-maple.c
new file mode 100644
index 0000000..e49ced1
--- /dev/null
+++ b/docz/static/maple/sheetjs-maple.c
@@ -0,0 +1,115 @@
+#include
+#include
+#include
+#include "maplec.h"
+#include "duktape.h"
+
+/* --- EXPORT_DECL macro from official example --- */
+
+#if !defined(EXPORT_DECL)
+#ifdef _MSC_VER
+#define EXPORT_DECL __declspec(dllexport)
+#else
+#define EXPORT_DECL
+#endif
+#endif
+
+/* --- the SheetJS + Duktape demo cover these machinations --- */
+
+#define FAIL_LOAD { \
+ duk_push_undefined(ctx); \
+ perror("Error in load_file"); \
+ return 1; \
+}
+
+static char *read_file(const char *filename, size_t *sz) {
+ FILE *f = fopen(filename, "rb");
+ if(!f) return NULL;
+ long fsize; { fseek(f, 0, SEEK_END); fsize = ftell(f); fseek(f, 0, SEEK_SET); }
+ char *buf = (char *)malloc(fsize * sizeof(char));
+ *sz = fread((void *) buf, 1, fsize, f);
+ fclose(f);
+ return buf;
+}
+
+static duk_int_t eval_file(duk_context *ctx, const char *filename) {
+ size_t len; char *buf = read_file(filename, &len);
+ if(!buf) FAIL_LOAD
+
+ duk_push_lstring(ctx, (const char *)buf, (duk_size_t)len);
+ duk_int_t retval = duk_peval(ctx);
+ duk_pop(ctx);
+ return retval;
+}
+
+static duk_int_t load_file(duk_context *ctx, const char *filename, const char *var) {
+ size_t len; char *buf = read_file(filename, &len);
+ if(!buf) FAIL_LOAD
+
+ duk_push_external_buffer(ctx);
+ duk_config_buffer(ctx, -1, buf, len);
+ duk_put_global_string(ctx, var);
+ return 0;
+}
+
+static duk_int_t save_file(duk_context *ctx, const char *filename, const char *var) {
+ duk_get_global_string(ctx, var);
+ duk_size_t sz;
+ char *buf = (char *)duk_get_buffer_data(ctx, -1, &sz);
+
+ if(!buf) return 1;
+ FILE *f = fopen(filename, "wb"); fwrite(buf, 1, sz, f); fclose(f);
+ return 0;
+}
+
+#define FAIL_DUK(cmd) { \
+ const char *errmsg = duk_safe_to_string(ctx, -1); \
+ duk_destroy_heap(ctx); \
+ MapleRaiseError2(kv, "error in %1 : %2", ToMapleString(kv, cmd), ToMapleString(kv, errmsg)); \
+ return NULL; \
+}
+
+#define DOIT(cmd) duk_eval_string_noresult(ctx, cmd);
+
+/* SheetToXLSX function */
+EXPORT_DECL ALGEB M_DECL SheetToXLSX( MKernelVector kv, ALGEB *args ) {
+ duk_int_t res = 0;
+
+ /* get filename */
+ if(MapleNumArgs(kv, (ALGEB)args) != 1) {
+ MapleRaiseError(kv, "must specify a filename");
+ return NULL;
+ }
+ if(!IsMapleString(kv, args[1])) {
+ MapleRaiseError(kv, "filename must be a string");
+ return NULL;
+ }
+ const char *filename = MapleToString(kv, args[1]);
+
+ /* initialize duktape */
+ duk_context *ctx = duk_create_heap_default();
+ /* duktape does not expose a standard "global" by default */
+ DOIT("var global = (function(){ return this; }).call(null);");
+
+ /* load SheetJS library */
+ res = eval_file(ctx, "shim.min.js");
+ if(res != 0) FAIL_DUK("shim load")
+ res = eval_file(ctx, "xlsx.full.min.js");
+ if(res != 0) FAIL_DUK("library load")
+
+ /* read file */
+ res = load_file(ctx, filename, "buf");
+ if(res != 0) FAIL_DUK("file load")
+ printf("Loaded file %s\n", filename);
+
+ /* parse workbook and write to XLSX */
+ DOIT("wb = XLSX.read(buf.slice(0, buf.length), {type:'buffer'});");
+ DOIT("newbuf = (XLSX.write(wb, {type:'array', bookType:'xlsx'}));");\
+
+ /* write file */
+ res = save_file(ctx, "sheetjsw.xlsx", "newbuf");\
+ if(res != 0) FAIL_DUK("save sheetjsw.xlsx")
+
+ /* return filename */
+ return ToMapleString(kv, "sheetjsw.xlsx");
+}