Crystal is a statically typed, compiled language with a Ruby-like syntax.
In LiveCodes, Crystal is compiled and run entirely in the browser by Crystal's compiler, compiled to WebAssembly. Nothing runs on a server.
Usage
Demo:
show code
- JS
- TS
- React
- Vue
- Svelte
- Solid
- Preact
- Web Components
import { createPlayground } from 'livecodes';
const options = {
"config": {
"mode": "simple",
"activeEditor": "script",
"script": {
"language": "crystal-wasm",
"content": "begin\n raise \"boom\"\nrescue ex : Exception\n puts \"caught: #{ex.message}\"\nend\n\nname = gets\nputs \"hello #{name}\"\n"
},
"tools": {
"status": "full"
}
}
};
createPlayground('#container', options);
import { createPlayground, type EmbedOptions } from 'livecodes';
const options: EmbedOptions = {
"config": {
"mode": "simple",
"activeEditor": "script",
"script": {
"language": "crystal-wasm",
"content": "begin\n raise \"boom\"\nrescue ex : Exception\n puts \"caught: #{ex.message}\"\nend\n\nname = gets\nputs \"hello #{name}\"\n"
},
"tools": {
"status": "full"
}
}
};
createPlayground('#container', options);
import LiveCodes from 'livecodes/react';
export default function App() {
const options = {
"config": {
"mode": "simple",
"activeEditor": "script",
"script": {
"language": "crystal-wasm",
"content": "begin\n raise \"boom\"\nrescue ex : Exception\n puts \"caught: #{ex.message}\"\nend\n\nname = gets\nputs \"hello #{name}\"\n"
},
"tools": {
"status": "full"
}
}
};
return (<LiveCodes {...options} />);
}
<script setup>
import LiveCodes from "livecodes/vue";
const options = {
"config": {
"mode": "simple",
"activeEditor": "script",
"script": {
"language": "crystal-wasm",
"content": "begin\n raise \"boom\"\nrescue ex : Exception\n puts \"caught: #{ex.message}\"\nend\n\nname = gets\nputs \"hello #{name}\"\n"
},
"tools": {
"status": "full"
}
}
};
</script>
<template>
<LiveCodes v-bind="options" />
</template>
<script>
import LiveCodes from 'livecodes/svelte';
const options = {
"config": {
"mode": "simple",
"activeEditor": "script",
"script": {
"language": "crystal-wasm",
"content": "begin\n raise \"boom\"\nrescue ex : Exception\n puts \"caught: #{ex.message}\"\nend\n\nname = gets\nputs \"hello #{name}\"\n"
},
"tools": {
"status": "full"
}
}
};
</script>
<LiveCodes {...options} />
import LiveCodes from 'livecodes/solid';
export default function App() {
const options = {
"config": {
"mode": "simple",
"activeEditor": "script",
"script": {
"language": "crystal-wasm",
"content": "begin\n raise \"boom\"\nrescue ex : Exception\n puts \"caught: #{ex.message}\"\nend\n\nname = gets\nputs \"hello #{name}\"\n"
},
"tools": {
"status": "full"
}
}
};
return (<LiveCodes {...options} />);
}
import LiveCodes from 'livecodes/preact';
export default function App() {
const options = {
"config": {
"mode": "simple",
"activeEditor": "script",
"script": {
"language": "crystal-wasm",
"content": "begin\n raise \"boom\"\nrescue ex : Exception\n puts \"caught: #{ex.message}\"\nend\n\nname = gets\nputs \"hello #{name}\"\n"
},
"tools": {
"status": "full"
}
}
};
return (<LiveCodes {...options} />);
}
<live-codes></live-codes>
<script type="module">
import "livecodes/web-components";
const playground = document.querySelector("live-codes");
playground.config = {
"mode": "simple",
"activeEditor": "script",
"script": {
"language": "crystal-wasm",
"content": "begin\n raise \"boom\"\nrescue ex : Exception\n puts \"caught: #{ex.message}\"\nend\n\nname = gets\nputs \"hello #{name}\"\n"
},
"tools": {
"status": "full"
}
};
</script>
The first run downloads about 22 MB of compressed compiler, linker and standard library, and compiles them; later runs reuse them.
Communication with JavaScript
The Crystal code is compiled and run in a web worker. The following helpers are available in the browser global livecodes.crystalWasm object:
livecodes.crystalWasm.input: The standard input passed to the program.
livecodes.crystalWasm.loaded: A promise that resolves when the compiler is loaded and the program has run once.
livecodes.crystalWasm.output: The program's output.
livecodes.crystalWasm.error: The compiler diagnostics, if the code failed to compile.
livecodes.crystalWasm.exitCode: The program's exit code.
livecodes.crystalWasm.run: A function that compiles and runs the code with new input. It takes a string as input and returns a promise that resolves with an object containing output, error and exitCode.
Example:
show code
- JS
- TS
- React
- Vue
- Svelte
- Solid
- Preact
- Web Components
import { createPlayground } from 'livecodes';
const options = {
"template": "crystal-wasm",
"params": {
"activeEditor": "markup"
}
};
createPlayground('#container', options);
import { createPlayground, type EmbedOptions } from 'livecodes';
const options: EmbedOptions = {
"template": "crystal-wasm",
"params": {
"activeEditor": "markup"
}
};
createPlayground('#container', options);
import LiveCodes from 'livecodes/react';
export default function App() {
const options = {
"template": "crystal-wasm",
"params": {
"activeEditor": "markup"
}
};
return (<LiveCodes {...options} />);
}
<script setup>
import LiveCodes from "livecodes/vue";
const options = {
"template": "crystal-wasm",
"params": {
"activeEditor": "markup"
}
};
</script>
<template>
<LiveCodes v-bind="options" />
</template>
<script>
import LiveCodes from 'livecodes/svelte';
const options = {
"template": "crystal-wasm",
"params": {
"activeEditor": "markup"
}
};
</script>
<LiveCodes {...options} />
import LiveCodes from 'livecodes/solid';
export default function App() {
const options = {
"template": "crystal-wasm",
"params": {
"activeEditor": "markup"
}
};
return (<LiveCodes {...options} />);
}
import LiveCodes from 'livecodes/preact';
export default function App() {
const options = {
"template": "crystal-wasm",
"params": {
"activeEditor": "markup"
}
};
return (<LiveCodes {...options} />);
}
<live-codes
template="crystal-wasm"></live-codes>
<script type="module">
import "livecodes/web-components";
const playground = document.querySelector("live-codes");
playground.params = {
"activeEditor": "markup"
};
</script>
Language Info
Name
crystal-wasm
Aliases and Extensions
cr, crystal
Editor
script
Compiler
Crystal's compiler, compiled to WebAssembly.
Version
Crystal v1.22.0
Custom Settings
Custom settings added to the property crystal-wasm are passed as a JSON object to the compiler during compile. Please check the documentation for full reference.
Please note that custom settings should be valid JSON (i.e. functions are not allowed).
Example:
args is passed to the program as its command-line arguments, and compileArgs is added to the crystal build command line.
Example:
Custom Settings
{
"crystal-wasm": {
"args": ["--flag", "value"],
"compileArgs": ["-Dpreview_mt"]
}
}
Live Reload
By default, new code changes are sent to the result page for re-evaluation without a full page reload, avoiding the need to reinitialize the Crystal environment. This behavior can be disabled by adding the code comment # __livecodes_reload__ to the Crystal code, which forces a full page reload.
This comment can be added in the hiddenContent property of the editor for embedded playgrounds.
Limitations
- Memory is never reclaimed —
wasm32 selects Crystal's no-GC allocator — so a long-running program will grow.
- No threads, sockets or subprocesses: WASI preview 1 has none, and a program is given an empty filesystem.
Example Usage
puts "hello from Crystal, compiled in your browser"
1.upto(5) do |n|
puts "#{n} squared is #{n * n}"
end
Starter Template
https://livecodes.io/?template=crystal-wasm
Links