Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 141 additions & 0 deletions site/source/docs/compiling/best_practices.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
.. _Best-Practices:

==============
Best Practices
==============

This guide provides recommendations and best practices for compiling C and C++
to the modern web using WebAssembly and Emscripten.

Following this guide when reporting bugs can also help speed up diagnosing and
fixing issues since you will be on the well-trodden path.

In some cases, Emscripten will guide you towards these best practices by
emitting warnings, but that is not always possible.


General Recommendations
=======================

- **Don't pass settings that are enabled by default.** To keep command lines
clean and concise, omit redundant options that are already default in modern
Emscripten (such as ``-sWASM=1``).
- **Prefer standard compiler flags** over Emscripten-specific ones where
possible (for example, prefer ``-pthread`` over ``-sUSE_PTHREADS`` and
``-m64`` over ``-sMEMORY64``).
- **Use simple comma-separated lists** for list-based settings (for example,
``-sEXPORTED_FUNCTIONS=_main,_malloc`` rather than JSON arrays like
``-sEXPORTED_FUNCTIONS=['_main','_malloc']``).
- **Avoid long lists on the command line**; Use the ``@filename`` instead (for

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- **Avoid long lists on the command line**; Use the ``@filename`` instead (for
- **Avoid long lists on the command line**; use a response file instead, with ``@filename`` (for

example, ``-sEXPORTED_FUNCTIONS=@exported_funcs.txt``).
- **Don't include the "=1" suffix for boolean flags.** For example, write
``-sSTRICT`` and ``-sALLOW_MEMORY_GROWTH`` rather than ``-sSTRICT=1`` or
``-sALLOW_MEMORY_GROWTH=1``.
- **Use separate compilation** by compiling ``.cpp`` sources to ``.o`` object

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would put all the things before this in a "lint" section. Separate compilation and C++/JS interop feel like substantially different things, choices about the build system and design.

files before linking rather than combining everything into a single monolithic
compiler invocation.
- **Avoid direct usage of C/C++ functions from JavaScript**; prefer higher-level
interfaces such as :js:func:`cwrap` and/or :ref:`embind` that support more
than purely numeric types.

@kripken kripken Aug 26, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still disagree with this. Direct usage is simply faster, and often works well. How about

  • Call directly between C/C++ and JavaScript where possible. When only passing around numeric types this is the most efficient thing to do.
  • For more complex things, like passing a std::vector between the languages, use embind.



Recommended Flags
=================

- ``-sSTRICT``: Opt into strict modern Emscripten behavior, disabling
deprecated or legacy compatibility features.
- ``-sEXPORT_ES6``: Output a modern ES6 module (``module.mjs``). This option
Comment thread
sbc100 marked this conversation as resolved.
implies ``-sMODULARIZE`` so the generated code will be encapsulated and not
impact the global namespace.
- ``-sENVIRONMENT=web``: Limit the runtime support to only the environments you
are targeting. This reduces code size by, for example, omitting Node.js and
compatibility code.
- ``-Werror -Wall``: Treat warnings as errors to catch C++ bugs and invalid
compiler settings early.
- ``-O3``, ``-Os``, or ``-Oz``: For release builds, choose ``-O3`` when runtime
performance is most critical, or ``-Os`` / ``-Oz`` when minimizing binary
payload size is the priority.
- ``-flto``: Enable Link-Time Optimization (LTO) during both the compilation and
linking steps of release builds for maximum runtime performance and size
reduction.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we recommend Closure here?


@kripken kripken Aug 26, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section overlaps with our Optimizing Code page. Perhaps link to there? Or perhaps have a section here for optimizations that summarizes that page?

In particular, while most of this list is optimizations, things like STRICT feel more like "lint" (group with "avoid -sWASM=1, and EXPORT_ES6 feels more like "design choices" (group with "separate the compile&link stages)


Separate Compilation Workflows
==============================

For non-trivial projects, always separate the compilation step (compiling source
files to object files) from the linking step (combining object files into the
final WebAssembly and JavaScript outputs). This enables incremental builds and
matches standard C/C++ development practices.

When using separate compilation, ensure that optimization flags and settings
that affect code generation (such as ``-flto``, ``-O3``, ``-g``, or
``-pthread``) are passed at **both** compile and link times.

**Compilation step (producing object files):**

.. code-block:: bash

em++ -O3 -flto -c main.cpp -o main.o $(CXXFLAGS)

**Linking step (producing the ES6 module and WebAssembly binary):**

.. code-block:: bash

em++ -O3 -flto -sSTRICT -sEXPORT_ES6 --bind main.o -o module.mjs $(LDFLAGS)


Debug vs. Release Profiles
--------------------------

When configuring build profiles, keep compile and link flags consistent within
each configuration:

- **Release Builds:** Use ``-Oz`` or ``-Os`` (or ``-O3`` for CPU-bound tasks)
combined with ``-flto``.
Comment thread
sbc100 marked this conversation as resolved.
- **Debug Builds:** Use ``-g`` when compiling and either ``-g``,
``-gline-tables-only``, or ``-gsource-map`` when linking. Avoid optimization
flags (such as ``-O2`` or ``-O3``) or ``-flto`` during debug builds for
faster compilation and accurate debugging.


Modern Web Workflows and Common Pitfalls
========================================

Interoperability with JavaScript
--------------------------------

When exposing C++ functionality to JavaScript, prefer :ref:`embind` (``--bind``)
over raw ``extern "C"`` functions. Embind naturally handles C++ classes,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This overlaps with the above text, and I have the same response.

overloaded functions, smart pointers, ``std::string``, and ``std::vector``
without requiring manual memory conversions or unsafe casting in JavaScript.

Asynchronous Code Execution and Main Thread Blocking
----------------------------------------------------

**Don't run long synchronous loops on the browser main thread.** The browser
uses cooperative multitasking; blocking the main UI thread prevents rendering
and freezes the web page.

- Restructure infinite loops to yield to the event loop using
:c:func:`emscripten_set_main_loop`
(see :ref:`emscripten-runtime-environment-howto-main-loop`).
- For synchronous-looking C++ code that must pause (or interact with
asynchronous JavaScript APIs such as ``fetch()`` or Web Promises) without
refactoring into callbacks, use :ref:`Asyncify <yielding_to_main_loop>`
(``-sASYNCIFY``) or JavaScript Promise Integration (``-sJSPI``).
- Offload heavy compute or blocking operations to background workers using
:doc:`multithreading and pthreads <../porting/pthreads>` (``-pthread``).

Virtual Filesystem and I/O
--------------------------

Standard C/C++ file operations (such as ``fopen`` or ``std::ifstream``) operate
on Emscripten's virtual in-memory filesystem (``MEMFS`` by default). See the
:ref:`file-system-overview` for an architectural overview.

- Do not assume direct access to the host file system.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't a "best practice" so much as a "how to" - ?

- For small temporary files, ``MEMFS`` is sufficient.
- For persistent client-side data storage across browser sessions, use
asynchronous storage backends such as :ref:`filesystem-api-idbfs` or the
:ref:`Filesystem-API`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps just link to the main filesystem page for filesystem recommendations? This could also summarize that page, if we want that (if so, perhaps mention WasmFS).

2 changes: 2 additions & 0 deletions site/source/docs/compiling/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Compiling and Running Projects

This section contains topics about building projects and running the output.

- :ref:`Best-Practices` covers recommended compiler flags, modern web workflows, and Dos and Don'ts for using Emscripten.
- :ref:`Building-Projects` shows how to use :ref:`emccdoc` as a drop-in replacement for *gcc* in your existing project.
- :ref:`WebAssembly` explains how Emscripten can be used to build WebAssembly files
- :ref:`Running-html-files-with-emrun` explains how to use *emrun* to run generated HTML pages in a locally launched web server.
Expand All @@ -19,6 +20,7 @@ This section contains topics about building projects and running the output.
.. toctree::
:hidden:

best_practices
Building-Projects
WebAssembly
Modularized-Output
Expand Down
4 changes: 0 additions & 4 deletions site/source/docs/porting/guidelines/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,3 @@ Emscripten can be used to compile almost any *portable* C/C++ code to JavaScript
api_limitations
function_pointer_issues
browser_limitations.rst




Loading