From JavaScript
You have two ways in, and they run the same engine. You can write BioLang, which is
shorter for pipelines and is what the rest of this documentation uses. Or you can stay in
JavaScript entirely and call the runtime through the
JavaScript SDK — your loops, your objects,
your try/catch, with the sequence work and statistics happening
in Rust either way.
This page shows the same computation both ways so you can decide per task. Every JavaScript snippet below was generated from the BioLang beside it and then executed and compared against it, so these are checked translations rather than plausible ones.
Key Differences at a Glance
| Feature | JavaScript | BioLang |
|---|---|---|
| Bindings | const / let |
let (immutable by default) |
| Chaining | Method chains, or nested calls | |> pipe, left to right |
| Anonymous functions | (x) => x * 2 |
|x| x * 2 |
| String interpolation | `n=${n}` |
f"n={n}" |
| Biological types | Strings, plus a library | dna"ACGT", rna, protein are language types |
| Tables | Arrays of objects, plus a library | Table is built in, with filter, mutate, arrange |
| Numbers | One number type |
Int and Float are distinct |
| Equality | === with coercion rules to remember |
==, no coercion |
GC content
BioLang
gc_content(dna"ATCGATCGATCGATCG")
# 0.5
JavaScript
bl.gcContent(bl.dna("ATCGATCGATCGATCG"));
// 0.5
Builtin names are available in both spellings, so bl.gc_content(...) works
too. Use whichever reads better in the surrounding code.
Filtering and transforming
BioLang
let xs = [1, 5, 12, 20]
xs |> filter(|x| x > 10) |> map(|x| x * 2) |> sum()
# 64
JavaScript
const xs = [1, 5, 12, 20];
bl.sum(bl.map(bl.filter(xs, (x) => x > 10), (x) => x * 2));
// 64
This is the one place the mapping is genuinely worse in JavaScript. A pipe reads left to right in the order the data flows; nested calls read inside out, so the first step is buried deepest. Ordinary JavaScript array methods are an option when the data is already in JavaScript — and for scalar loops they are also faster, because V8 beats a tree-walking interpreter. Reach for the BioLang builtins when the operation is one you would not want to reimplement.
Tables
BioLang
let t = table({gene: ["TP53", "BRCA1", "EGFR"], expr: [142.5, 87.3, 210.0]})
t |> filter(|r| r.expr > 100.0) |> nrow()
# 2
JavaScript
const t = bl.table({ gene: ["TP53", "BRCA1", "EGFR"], expr: [142.5, 87.3, 210.0] });
bl.nrow(bl.filter(t, (r) => r.expr > 100.0));
// 2
A plain JavaScript object of columns becomes a BioLang Table, and rows come
back as objects whose fields you can read normally. Nothing needs to be reshaped by hand
at the boundary.
Functions
BioLang
fn tm(primer) {
return 64.9 + 41.0 * (gc_content(primer) - 0.164)
}
round(tm(dna"ACGTACGTACGT"), 2)
JavaScript
function tm(primer) {
return 64.9 + 41.0 * (bl.gcContent(primer) - 0.164);
}
bl.round(tm(bl.dna("ACGTACGTACGT")), 2);
A BioLang function becomes a JavaScript function. Control flow stays yours: write
if, for and try/catch the way you
normally would, and call into BioLang for the parts that are worth calling into.
String interpolation
BioLang
let counts = [10, 12, 14, 11]
f"mean={mean(counts)}"
JavaScript
const counts = [10, 12, 14, 11];
`mean=${bl.mean(counts)}`;
Which should I write?
Write BioLang when the work is a pipeline — several stages of filter, map and summarise over sequences, reads or tables. That is what the pipe operator and the built-in biological types exist for, and the BioLang version is usually half the length.
Write JavaScript when BioLang is one step inside a larger program you already have: a web app, an API server, a notebook UI, an Electron shell. You keep your own tooling, debugger and error handling, and reach into the runtime for the bioinformatics.
You are not choosing once. bl.run(source) takes BioLang source from inside
JavaScript, and bl.transpileJavaScript(source) converts an existing
.bl file into the direct JavaScript API. Mixing the two in one program is
normal.
Next
- JavaScript SDK — sessions, values, handles, callbacks and what the build checks.
- Verified equivalents — the same computation in BioLang, JavaScript, Python and R, each one executed and compared.
- Embedding (WASM) — the raw module, if you do not want the package.
- Pipes & operators — the part of BioLang with no JavaScript equivalent.