{"id":16125715,"url":"https://github.com/dy/piezo","last_synced_at":"2025-07-19T06:06:08.330Z","repository":{"id":66081305,"uuid":"465540569","full_name":"dy/piezo","owner":"dy","description":"Prototype language for signal processing","archived":false,"fork":false,"pushed_at":"2025-03-17T15:01:04.000Z","size":2477,"stargazers_count":27,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-07-19T00:03:47.237Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://dy.github.io/piezo/examples","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-03-03T02:31:03.000Z","updated_at":"2025-07-18T17:28:43.000Z","dependencies_parsed_at":"2024-06-22T14:59:56.966Z","dependency_job_id":"bebe243e-a0ad-4552-a91e-520b4b069f46","html_url":"https://github.com/dy/piezo","commit_stats":{"total_commits":812,"total_committers":2,"mean_commits":406.0,"dds":0.007389162561576401,"last_synced_commit":"c3386542cf075567bc2751038f2d61e661f8b057"},"previous_names":["dy/lino","audio-lab/auro","audio-lab/aux","audio-lab/lino","audio-lab/shym","audio-lab/sone","audio-lab/syne","dy/melo","dy/ylang","dy/piezo"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/dy/piezo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dy%2Fpiezo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dy%2Fpiezo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dy%2Fpiezo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dy%2Fpiezo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dy","download_url":"https://codeload.github.com/dy/piezo/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dy%2Fpiezo/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265896078,"owners_count":23845422,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-09T21:30:55.436Z","updated_at":"2025-07-19T06:06:08.317Z","avatar_url":"https://github.com/dy.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# piezo ![stability](https://img.shields.io/badge/stability-experimental-black) [![test](https://github.com/dy/piezo/actions/workflows/test.yml/badge.svg)](https://github.com/dy/piezo/actions/workflows/test.yml)\n\nPrototype language designed for signal processing, synthesis and analysis.\u003cbr/\u003e\nProject is early experimental stage, design decisions must be consolidated.\n\n\u003c!-- [Examples](https://dy.github.io/piezo/examples/) | [Motivation](#motivation) --\u003e\n\n## Reference\n\n```\n;; Operators\n+ - * / % -- ++               ;; arithmetical (float)\n** %% //                      ;; power, unsigned mod, flooring div\n\u0026 | ^ ~ \u003e\u003e \u003c\u003c                 ;; binary (integer)\n\u003c\u003c\u003c \u003e\u003e\u003e                       ;; rotate left, right\n\u0026\u0026 || !                       ;; logical\n\u003e \u003e= \u003c \u003c= == !=               ;; comparisons (boolean)\n?:                            ;; condition, switch\nx[i] x[]                      ;; member access, length\na..b a.. ..b ..               ;; ranges\n|\u003e _                          ;; pipe/loop/map, topic reference\n./ ../ /                      ;; continue/skip, break/stop, return\n\u003e\u003c \u003c\u003e                         ;; inside, outside\n-\u003c -/ -*                      ;; clamp, normalize, lerp\n\n;; Numbers\n16, 0x10, 0o755, 0b0;         ;; int, hex, oct or binary\n16.0, .1, 2e-3;               ;; float\n1k = 1000; 1pi = 3.1415926;   ;; units\n1s = 44100; 1m = 60s;         ;; eg: sample indexes\n10.1k, 2pi, 1m30s;            ;; 10100, 6.283..., 66150\n\n;; Variables\nfoo=1, bar=2.0;               ;; declare vars\nAbC, $0, Δx, x@1, A#;         ;; names permit alnum, unicodes, _$#@\nfoo == Foo, bar == bAr;       ;; case-insensitive\ndefault=1, eval=fn, else=0;   ;; no reserved words\ntrue = 0b1, false = 0b0;      ;; eg: alias bools\ninf = 1/0, nan = 0/0;         ;; eg: alias infinity, NaN\n\n;; Ranges\n0..10;                        ;; from 1 to 9 (10 exclusive)\n0.., ..10, ..;                ;; open ranges\n10..1;                        ;; reverse range\n1.08..108.0;                  ;; float range\n(a-1)..(a+1);                 ;; computed range\n0..3 * 2;                     ;; mapped range: 0*2, 1*2, 2*2\n(a,b,c) = 0..3 * 2;           ;; destructure: a=0, b=2, c=4\na \u003e\u003c 0..10, a \u003c\u003e 0..10;       ;; inside(a, 0, 10), outside(a, 0, 10);\na -\u003c 0..10, a -\u003c= 0..10;      ;; clamp(a, 0, 10), a = clamp(a, 0, 10)\na -\u003c ..10, a -\u003c 10..;         ;; min(a, 10), max(a, 10)\na -* 0..10, a -/ 0..10;       ;; lerp(a, 0, 10), normalize(a, 0, 10)\n\n;; Groups\n(a,b,c) = (1,2,3);            ;; assign: a=1, b=2, c=3\n(a,b) = (b,a);                ;; swap\n(a,b,c) = d;                  ;; duplicate: a=d, b=d, c=d\n(a,,b) = (c,d,e);             ;; skip: a=c, b=e\n(a,b) + (c,d);                ;; group binary: a+c, b+d\n(a, b, c)++;                  ;; group unary: a++, b++, c++\n(a,b)[1] = c[2,3];            ;; props: a[1]=c[2], b[1]=c[3]\n(a,..,z) = (1,2,3,4);         ;; pick: a=1, z=4\na = (b,c,d);                  ;; pick first: a=b; see loops\n\n;; Arrays\nm = [..10];                   ;; array of 10 elements\nm = [..10 |\u003e 2];              ;; filled with 2\nm = [1,2,3,4];                ;; array of 4 elements\nm = [n[..]];                  ;; copy n\nm = [1, 2..4, 5];             ;; mixed definition\nm = [1, [2, 3, [4, m]]];      ;; nested arrays (tree)\nm = [0..4 |\u003e _ ** 2];         ;; list comprehension\n(a, z) = (m[0], m[-1]);       ;; get by index\n(b, .., z) = m[1, 2..];       ;; get multiple values\nlength = m[];                 ;; get length\nm[0] = 1;                     ;; set value\nm[2..] = (1, 2..4, n[1..3]);  ;; set multiple values from offset 2\nm[1,2] = m[2,1];              ;; swap\nm[0..] = m[-1..];             ;; reverse\nm[0..] = m[1..,0];            ;; rotate\n\n;; Strings\nhi=\"Hello\";                   ;; creates static array\nstring=\"$\u003chi\u003e, world!\";       ;; interpolate: \"hello world\"\nstring[1, 3..5, -2];          ;; pick elements: 'e', 'lo', 'd'\nstring[0..5];                 ;; substring: 'Hello'\nstring[-1..0];                ;; reversed: '!dlrow ,olleH'\nstring[];                     ;; length: 13\n\n;; Conditions\na ? b;                        ;; if a then b (else 0)\na ?: b;                       ;; if (a then 0) else b\nsign = a \u003c 0 ? -1 : +1;       ;; ternary\nval = (                       ;; switch\n  a == 1 ? ./log(1);          ;; if a == 1 return log(1)\n  a \u003e\u003c 2..4 ? ./log(2);       ;; if a in 2..4 return log(2)\n  log(3)                      ;; otherwise\n);\na ?/ b;                       ;; early return: if a then return b\n\n;; Loops\n(a, b, c) |\u003e f(_);            ;; for each item in a, b, c do f(item)\n(i = 10..) |\u003e (               ;; descend over range\n  i \u003c 5 ? a ./;               ;; if item \u003c 5 skip (continue)\n  i \u003c 0 ? a ../;              ;; if item \u003c 0 stop (break)\n);                            ;;\nx[..] |\u003e f(_) |\u003e g(_);        ;; pipeline sequence\n(i = 0..w) |\u003e (               ;; nest iterations\n  (j = 0..h) |\u003e f(i, j);      ;; f(x,y)\n);                            ;;\n((a,b) = 0..10) |\u003e a+b;       ;; iterate pairs\n(x,,y) = (a,b,c) |\u003e _ * 2;    ;; capture result x = a*2, y = c*2;\n.. |\u003e i \u003c 10 ? i++ : ../;     ;; while i \u003c 10 i++\n\n;; Functions\ndouble(n) = n*2;              ;; define a function\ntimes(m = 1, n -\u003c 1..) = (    ;; optional, clamped arg\n  n == 0 ? /n;                ;; early return\n  m * n;                      ;; returns last statement\n);                            ;;\ntimes(3,2);                   ;; 6\ntimes(4), times(,5);          ;; 4, 5: optional, skipped arg\ndup(x) = (x,x);               ;; return multiple\n(a,b) = dup(b);               ;; destructure\na=1,b=1; x()=(a=2;b=2); x();  ;; a==1, b==2: first statement declares locals\nfn() = ( x; log(x) );         ;; defer: calls log after returning x\nf(a, cb) = cb(a[0]);          ;; array, func args\na() = ( *i=0; *i++ );         ;; state var: i persists value\na(), a();                     ;; 0,1\na.i = 0;                      ;; reset state\n*a1 = a;                      ;; clone function\na(), a(); a1(), a1();         ;; 0,1; 0,1;\n\n;; Export\nx, y, z;                      ;; exports last statement\n```\n\n\u003c!--\n## Examples\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eGain\u003c/strong\u003e\u003c/summary\u003e\n\nProvides k-rate amplification for block of samples.\n\n```\ngain(                             ;; define a function with block, volume arguments.\n  block,                          ;; block is a array argument\n  volume -\u003c 0..100                ;; volume is limited to 0..100 range\n) = (\n  block[..] |\u003e= # * volume        ;; multiply each sample by volume value\n);\n\ngain([0..5 * 0.1], 2);            ;; 0, .2, .4, .6, .8, 1\n```\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eBiquad Filter\u003c/strong\u003e\u003c/summary\u003e\n\nA-rate (per-sample) biquad filter processor.\n\n```\n1pi = pi;                         ;; define pi units\n1s = 44100;                       ;; define time units in samples\n1k = 10000;                       ;; basic si units\n\nlpf(                              ;; per-sample processing function\n  x0,                             ;; input sample value\n  freq = 100 -\u003c 1..10k,            ;; filter frequency, float\n  Q = 1.0 -\u003c 0.001..3.0            ;; quality factor, float\n) = (\n  *(x1, y1, x2, y2) = 0;          ;; define filter state\n\n  ;; lpf formula\n  w = 2pi * freq / 1s;\n  sin_w, cos_w = sin(w), cos(w);\n  a = sin_w / (2.0 * Q);\n\n  b0, b1, b2 = (1.0 - cos_w) / 2.0, 1.0 - cos_w, b0;\n  a0, a1, a2 = 1.0 + a, -2.0 * cos_w, 1.0 - a;\n\n  b0, b1, b2, a1, a2 *= 1.0 / a0;\n\n  y0 = b0*x0 + b1*x1 + b2*x2 - a1*y1 - a2*y2;\n\n  x1, x2 = x0, x1;            ;; shift state\n  y1, y2 = y0, y1;\n\n  y0                              ;; return y0\n);\n\n;; i = [0, .1, .3] |\u003e lpf(i, 108, 5);\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eZZFX\u003c/strong\u003e\u003c/summary\u003e\n\nGenerates ZZFX's [coin sound](https://codepen.io/KilledByAPixel/full/BaowKzv) `zzfx(...[,,1675,,.06,.24,1,1.82,,,837,.06])`.\n\n```\n1pi = pi;\n1s = 44100;\n1ms = 1s / 1000;\n\n;; define waveform generators\noscillator = [\n  saw(phase) = (1 - 4 * abs( round(phase/2pi) - phase/2pi )),\n  sine(phase) = sin(phase)\n];\n\n;; applies adsr curve to sequence of samples\nadsr(\n  x,\n  a -\u003c 1ms..,                    ;; prevent click\n  d,\n  (s, sv=1),                    ;; optional group-argument\n  r\n) = (\n  *i = 0;                       ;; internal counter, increments after fn body\n  t = i / 1s;\n\n  total = a + d + s + r;\n\n  y = t \u003e= total ? 0 : (\n    t \u003c a ? t/a :               ;; attack\n    t \u003c a + d ?                 ;; decay\n    1-((t-a)/d)*(1-sv) :        ;; decay falloff\n    t \u003c a  + d + s ?            ;; sustain\n    sv :                        ;; sustain volume\n    (total - t)/r * sv\n  ) * x;\n  i++;\n  y\n);\n\n;; curve effect\ncurve(x, amt~0..10=1.82) = (sign(x) * abs(x)) ** amt;\n\n;; coin = triangle with pitch jump, produces block\ncoin(freq=1675, jump=freq/2, delay=0.06, shape=0) = (\n  *out=[..1024];\n  *i=0;\n  *phase = 0;                   ;; current phase\n  t = i / 1s;\n\n  ;; generate samples block, apply adsr/curve, write result to out\n  ..  |\u003e oscillator[shape](phase)\n      |\u003e adsr(_, 0, 0, .06, .24)\n      |\u003e curve(_, 1.82)\n      |\u003e out[..] = _;\n\n  i++;\n  phase += (freq + (t \u003e delay \u0026\u0026 jump)) * 2pi / 1s;\n)\n```\n\n\u003c/details\u003e\n--\u003e\n\u003c!--\n## [Freeverb](https://github.com/opendsp/freeverb/blob/master/index.js)\n\n```\n\u003c./combfilter.s#comb\u003e;\n\u003c./allpass.s#allpass\u003e;\n\n1s = 44100;\n\n(a1,a2,a3,a4) = (1116,1188,1277,1356);\n(b1,b2,b3,b4) = (1422,1491,1557,1617);\n(p1,p2,p3,p4) = (225,556,441,341);\n\n;; TODO: stretch\n\nreverb(input, room=0.5, damp=0.5) = (\n  *combs_a = a0,a1,a2,a3 | a: stretch(a),\n  *combs_b = b0,b1,b2,b3 | b: stretch(b),\n  *aps = p0,p1,p2,p3 | p: stretch(p);\n\n  combs = (\n    (combs_a | x -\u003e comb(x, input, room, damp) |: (a,b) -\u003e a+b) +\n    (combs_b | x -\u003e comb(x, input, room, damp) |: (a,b) -\u003e a+b)\n  );\n\n  (combs, aps) | (input, coef) -\u003e p + allpass(p, coef, room, damp)\n);\n```\n\nFeatures:\n\n* _multiarg pipes_ − pipe can consume groups. Depending on arity of target it can act as convolver: `a,b,c | (a,b) -\u003e a+b` becomes  `(a,b | (a,b)-\u003ea+b), (b,c | (a,b)-\u003ea+b)`.\n* _fold operator_ − `a,b,c |: fn` acts as `reduce(a,b,c, fn)`, provides efficient way to reduce a group or array to a single value.\n\n### [Floatbeat](https://dollchan.net/bytebeat/index.html#v3b64fVNRS+QwEP4rQ0FMtnVNS9fz9E64F8E38blwZGvWDbaptCP2kP3vziTpumVPH0qZyXzfzHxf8p7U3aNJrhK0rYHfgHAOZZkrlVVu0+saKbd5dTXazolRwnvlKuwNvvYORjiB/LpyO6pt7XhYqTNYZ1DP64WGBYgczuhAQgpiTXEtIwP29pteBZXqwTrB30jwc7i/i0jX2cF8g2WIGKlhriTRcPjSvcVMBn5NxvgCOc3TmqZ7/IdmmEnAMkX2UPB3oMHdE9WcKqVK+i5Prz+PKa98uOl60RgE6zP0+wUr+qVpZNsDUjKhtyLkKvS+LID0FYVSrJql8KdSMptKKlx9eTIbcllvdf8HxabpaJrIXEiycV7WGPeEW9Y4v5CBS07WBbUitvRqVbg7UDtQRRG3dqtZv3C7bsBbFUVcALvwH86MfSDws62fD7CTb0eIghE/mDAPyw9O9+aoa9h63zxXl2SW/GKOFNRyxbyF3N+FA8bPyzFb5misC9+J/XCC14nVKfgRQ7RY5ivKeKmmjOJMaBJSbEZJoiZZMuj2pTEPGunZhqeatOEN3zadxrXRmOw+AA==)\n\nTranspiled floatbeat/bytebeat song:\n\n```\n\u003cmath#asin,sin,pi\u003e;\n\n1s = 44100;\n\nfract(x) = x % 1;\nmix(a, b, c) = (a * (1 - c)) + (b * c);\ntri(x) = 2 * asin(sin(x)) / pi;\nnoise(x) = sin((x + 10) * sin((x + 10) ** (fract(x) + 10)));\nmelodytest(time) = (\n  melodyString = \"00040008\",\n  melody = 0;\n\n  0..5 \u003c| (\n    melody += tri(\n      time * mix(\n        200 + (# * 900),\n        500 + (# * 900),\n        melodyString[floor(time * 2) % melodyString[]] / 16\n      )\n    ) * (1 - fract(time * 4))\n  );\n\n  melody\n)\nhihat(time) = noise(time) * (1 - fract(time * 4)) ** 10;\nkick(time) = sin((1 - fract(time * 2)) ** 17 * 100);\nsnare(time) = noise(floor((time) * 108000)) * (1 - fract(time + 0.5)) ** 12;\nmelody(time) = melodytest(time) * fract(time * 2) ** 6 * 1;\n\nsong() = (\n  *t=0; @t++; time = t / 1s;\n  (kick(time) + snare(time)*.15 + hihat(time)*.05 + melody(time)) / 4\n)\n```\n\nFeatures:\n\n* _loop operator_ − `cond \u003c| expr` acts as _while_ loop, calling expression until condition holds true. Produces sequence as result.\n* _string literal_ − `\"abc\"` acts as array with ASCII codes.\n* _length operator_ − `items[]` returns total number of items of either an array, group, string or range.\n--\u003e\n\u003c!--\n* [Freeverb](/examples/freeverb.s)\n* [Floatbeat](/examples/floatbeat.s)\n* [Complete ZZFX](/examples/zzfx.s)\n\nSee [all examples](/examples) --\u003e\n\n\u003c!--\n## Usage\n\n_piezo_ is available as CLI or JS package.\n\n`npm i -g piezo`\n\n### CLI\n\n```sh\npiezo source.z -o dest.wasm\n```\n\nThis produces compiled WASM binary.\n\n### JS\n\n```js\nimport piezo from 'piezo'\n\n// create wasm arrayBuffer\nconst buffer = piezo.compile(`\n  n=1;\n  mult(x) = x*PI;\n  arr=[1, 2, sin(1.08)];\n  mult, n, arr;\n`, {\n  // js objects or paths to files\n  imports: {\n    math: Math,\n    mylib: './path/to/my/lib.z'\n  },\n  // optional: import memory\n  memory: true\n})\n\n// create wasm instance\nconst module = new WebAssembly.Module(buffer)\nconst instance = new WebAssembly.Instance(module, {\n  imports: {\n    math: Math,\n    // imported memory\n    memory: new WebAssembly.Memory({\n      initial: 10,\n      maximum: 100,\n    })\n  }\n})\n\n// use API\nconst { mult, n, arr, memory } = instance.exports\n\n// number exported as global\nn.value = 2;\n\n// function exported directly\nmult(108)\n\n// array is a pointer to memory, get values via\nconst arrValues = new Float64Array(arr, memory)\n```\n--\u003e\n\n\u003c!--\n## Motivation\n\nAudio processing has no cross-platform solution, various environments deal with audio differently, some don't have audio processing at all. _Web Audio API_ is unreliable - it has unpredictable pauses, glitches and so on, so \u003cq\u003eaudio is better handled in WASM worklet\u003c/q\u003e ([@stagas](https://github.com/stagas)).\n\n_Piezo_ attempts to fill that gap, providing a common layer. It is also a personal attempt on language design - rethinking parts and providing safe haven. WASM target gives max performance and compatibility - browsers, [audio/worklets](https://developer.mozilla.org/en-US/docs/Web/API/AudioWorkletProcessor/process), web-workers, nodejs, [embedded systems](https://github.com/bytecodealliance/wasm-micro-runtime) etc.\n\n\n### Principles\n\n* Maximal expressivity with minimal syntax.\n* _Familiar_: common syntax, clear patterns for new operators.\n* _Performant_: fast compile, fast execution – good for live envs.\n* _No keywords_: chars for vars, symbols for operators – good for i18l code.\n* _No runtime_: statically analyzable, no OOP, no dynamic structures, no lamdas, no nested scopes.\n* _No waste_: linear memory, fixed heap, no GC.\n* _Inferred types_: derived by usage – focus on logic over language.\n* _Explicit_: no implicit globals, no wildcard imports, no hidden file conventions.\n* _Space-agnostic_: spaces and newlines can be removed or added freely (eg. for compression or formatting).\n* _Case-agnostic_: case changes don't break code (eg. `sampleRate` vs `samplerate`).\n* _Normalized syntax_: no complex parsing rules – just unary, binary or n-ary operators.\n* _Readable output_: produces readable WebAssembly text (eg. can serve as meta-language).\n* _Low-level_: no fancy features beyond math and buffers, embeddable.\n* _Minimal footprint_: minimally possible produced WASM output, no heavy workarounds.\n\n--\u003e\n\n\u003c!--\n## Projects using piezo\n\n* [web-audio-api](https://github.com/audiojs/web-audio-api)\n* [audiojs](https://github.com/audiojs/)\n--\u003e\n\n### Philosophy\n\n\n\n### Inspiration\n\n[_mono_](https://github.com/stagas/mono), [_zzfx_](https://killedbyapixel.github.io/ZzFX/), [_bytebeat_](https://sarpnt.github.io/bytebeat-composer/), [_glitch_](https://github.com/naivesound/glitch), [_hxos_](https://github.com/stagas/hxos), [_min_](https://github.com/r-lyeh/min), [_roland_](https://github.com/DenialAdams/roland), [_porffor_](https://github.com/CanadaHonk/porffor)\n\n### Acknowledgement\n\n* @stagas for initial drive \u0026 ideas\n\n\u003cp align=center\u003e\u003ca href=\"https://github.com/krsnzd/license/\"\u003e🕉\u003c/a\u003e\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdy%2Fpiezo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdy%2Fpiezo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdy%2Fpiezo/lists"}