Turn ๐ into
:elephant:and back, with a whole PHP interpreter compiled to WebAssembly, running in your browser. Because we could.
Emoji Converter converts emoji to shortcodes (GitHub, GitLab, Slack, text), to their names in 136 languages, or to nothing at all, and back to emoji. The conversion is done by symfony/emoji, in PHP, in your browser: there is no server, your text never leaves the tab.
Mostly. symfony/emoji converts with a good old strtr(), and we ship 8 MB of
WebAssembly to call it. But it works, it is fast (a few milliseconds per
conversion, PHP boot included), and it was a good excuse to build PHP with the
intl extension for the browser, which is the actually hard part.
PHP is compiled by php-wasm-builder,
and castor app:build puts everything in
website/public/build/:
php-web.wasmandphp-web.mjs: PHP 8.5 with the intl extension, compiled with Emscripten, and the application (src/,website/src/andvendor/), embedded in/app. ICU is statically linked, so there is nointl.soto load withdlopen(): it compiles WebAssembly synchronously, which Chrome refuses on the main thread;emoji/: the catalogs of symfony/emoji. They weigh ~37 MB, so the page only fetches the ones it needs (see php.js).
The ICU data (~30 MB) is not shipped: symfony/emoji only needs the
\Transliterator class. Anything else that needs the ICU data (Collator,
NumberFormatter, IntlDateFormatterโฆ) fails.
For each conversion, the page runs
website/src/convert.php with runFile(), gives
it the request on stdin, and gets the result on stdout, and the errors on
stderr. PHP is started and shut down on each conversion, see the
JavaScript API of
php-wasm-builder.
Fibers and proc_open() are not supported: Emscripten does not provide the
functions they rely on.
The converter is a thin layer over symfony/emoji:
use EmojiConverter\EmojiConverter;
$converter = new EmojiConverter();
$converter->fromEmoji('github', 'Hello ๐ ๐'); // Hello :wave: :elephant:
$converter->fromEmoji('fr', 'Hello ๐ ๐'); // Hello signe de la main รฉlรฉphant
$converter->toEmoji('slack', 'Hello :wave: :elephant:'); // Hello ๐ ๐
$converter->toEmoji('fr', 'Un chat noir, pas un achat'); // Un ๐โโฌ, pas un achatsymfony/emoji only converts shortcodes back to emoji: names are ordinary words,
replacing them with strtr() would also replace them inside other words. The
converter replaces whole words only, and prefers the longest name (โchat noirโ
over โchatโ).
You need PHP 8.5+ with intl, Composer, Castor, Docker with Buildx, and Node.js 24+.
castor app:install # Install the dependencies
castor app:tests # Run the tests
castor qa:cs # Fix coding standards
castor qa:phpstan # Run PHPStan
castor app:build # Build PHP WebAssembly with the application
castor app:smoke # Check that what is built boots, and that the page converts
castor app:serve # Serve the website on http://localhost:9999The first build takes a few minutes (ICU and PHP are compiled), then only the
application is packed again. The website is deployed on GitHub Pages on each
push on main, once the tests pass.
Emoji Converter is released under the MIT license, see LICENSE. The fonts, Bricolage Grotesque and Space Mono, are released under the SIL Open Font License.



