> For the complete Mojo documentation index, see [llms.txt](/llms.txt).
> Markdown versions of all pages are available by appending .md to any URL (e.g. /docs/manual/basics.md).

# Jupyter notebooks

[Jupyter notebooks](https://jupyter.org) provide a web-based environment for
creating and sharing Mojo computational documents. They combine code, results,
and explanation so readers explore what you built, how you built it, and why it
matters.

You can run Mojo language notebooks locally or in GPU-backed Google Colab
environments to accelerate workloads. For teaching, learning, and exploration,
notebooks provide a hands-on, iterative workflow.

This page assumes you'll work with Mojo notebooks in one of two ways:

- **Google Colab** <br />
  Fast setup, optional GPU acceleration, ideal for quick experiments and for
  learning GPU programming when you don't have a compatible GPU-enabled
  computer on-hand.
- **Local JupyterLab** <br />
  Private environment with full control of code, data, and dependencies.

Both options use the same notebook model and the same Mojo cell magic.

## Using Mojo on Google Colab

1. Create a Notebook:

    Visit [Google Colab](https://colab.google) and create a new notebook.

2. Install Mojo:

   For the nightly release:

    ```python
    !pip install --pre mojo --extra-index-url https://whl.modular.com/nightly/simple/
    ```

   For the stable release:

    ```python
    !pip install mojo
    ```

   Wait for the "Successfully installed" message.

3. Enable Mojo:

    In the first cell, run:

    ```python
    import mojo.notebook
    ```

    This adds the `%%mojo` cell magic, so you can compile and
    run Mojo code.

    Your Colab notebook is now ready to run Mojo programs.

## Using Mojo in Local Jupyter Notebooks

Local notebooks use `pixi` to manage an environment with
Jupyter and Mojo.

1. Create a project:

    ```shell
    pixi init notebooks \
        -c https://conda.modular.com/max-nightly/ \
        -c conda-forge
    cd notebooks
    pixi shell
    ```

    This creates a project directory and enters the Pixi shell.

2. Install required tools:

    ```shell
    pixi add mojo jupyterlab ipykernel
    ```

    This installs:
   - Mojo
   - JupyterLab
   - The Python kernel required for notebook execution

3. Start JupyterLab:

    ```shell
    jupyter lab
    ```

    JupyterLab opens in your browser.

4. Create a Python-backed notebook:

    In your web browser:

   - Select _File > New > Notebook_.
   - Choose the _Python_ kernel.

5. Enable Mojo support:

    In the first cell, run:

    ```python
    import mojo.notebook
    ```

    This registers the `%%mojo` magic command.
    Your local environment is now ready for interactive Mojo development.

## Writing and running Mojo code

Mojo code runs inside notebook cells marked with the `%%mojo` directive.
Each Mojo cell must contain a complete program, including a `main()` function.

### Example: Hello Mojo

```mojo
%%mojo

def main():
  print("Hello Mojo")
```

Output:

```output
Hello Mojo
```

### Example: Parameterized compilation

```mojo
%%mojo

# Compiler-parameterized function
def repeat[count: Int](https://mojolang.org/docs/tools/msg: String.md):
    comptime for i in range(count):
        print(msg)

# Compiler-argumented function
def threehello():
    repeat[3](https://mojolang.org/docs/tools/"Hello 🔥!".md)

# Run
def main():
    threehello()
```

Output:

```output
Hello 🔥!
Hello 🔥!
Hello 🔥!
```

## Using Mojo with GPU support

Google Colab offers GPU-backed runtimes so you can run Mojo GPU
examples even without local hardware. The specific accelerator
available depends on your Colab tier; see
[GPU compatibility](/docs/requirements/#gpu-compatibility) for the list
of accelerators supported by Mojo. Before running GPU code, select
_Runtime > Change runtime type > [GPU]_.

### Example: GPU Hello World

```mojo
%%mojo

from std.gpu.host import DeviceContext

def kernel():
    print("Hello from the GPU")

def main() raises:
    # Launch GPU kernel
    with DeviceContext() as ctx:
        ctx.enqueue_function[kernel](https://mojolang.org/docs/tools/grid_dim=1, block_dim=1.md)
        ctx.synchronize()
```

Output:

```output
Hello from the GPU
```

### Example: Hello writing

This example writes a value to device memory and reads it back on the host:

```mojo
%%mojo
from std.memory import UnsafePointer
from std.gpu.host import DeviceContext

comptime `✅`: Int32 = 1
comptime `❌`: Int32 = 0

def kernel(value: UnsafePointer[Scalar[DType.int32], MutAnyOrigin]):
    value[0] = `✅`

def main() raises:
    with DeviceContext() as ctx:
        # Build it
        var out = ctx.enqueue_create_buffer[DType.int32](https://mojolang.org/docs/tools/1.md)
        out.enqueue_fill(`❌`)

        # Run it
        ctx.enqueue_function[kernel](https://mojolang.org/docs/tools/out, grid_dim=1, block_dim=1.md)

        # Report the result
        with out.map_to_host() as out_host:
            print("GPU responded:", \
                "👋, 🔥" if out_host[0] == `✅` else "😢")
```

Output:

```output
👋, 🔥
```

### Example: GPU vector addition

This example runs elementwise vector addition on the GPU.
Each GPU thread updates one element.

```mojo
%%mojo

from std.gpu import thread_idx
from std.gpu.host import DeviceContext
from layout import TileTensor, row_major
from std.sys import has_accelerator

comptime VECTOR_WIDTH = 10
comptime layout = row_major[VECTOR_WIDTH]()
comptime active_dtype = DType.uint8

# Elementwise vector addition on GPU threads
def vector_addition(
    left: TileTensor[active_dtype, type_of(layout), MutAnyOrigin],
    right: TileTensor[active_dtype, type_of(layout), MutAnyOrigin],
    output: TileTensor[active_dtype, type_of(layout), MutAnyOrigin],
):
    var idx = thread_idx.x
    output[idx] = left[idx] + right[idx]

def main() raises:
    # Ensure a supported GPU (NVIDIA or AMD) is available
    comptime assert has_accelerator(), "This example requires a supported GPU"

    # Create GPU device context
    var ctx = DeviceContext()

    # Allocate buffers and tensors for left and right operands, and output
    var left_buffer = ctx.enqueue_create_buffer[active_dtype](https://mojolang.org/docs/tools/VECTOR_WIDTH.md)
    var left_tensor = TileTensor(left_buffer, layout)

    var right_buffer = ctx.enqueue_create_buffer[active_dtype](https://mojolang.org/docs/tools/VECTOR_WIDTH.md)
    var right_tensor = TileTensor(right_buffer, layout)

    var output_buffer = ctx.enqueue_create_buffer[active_dtype](https://mojolang.org/docs/tools/VECTOR_WIDTH.md)
    var output_tensor = TileTensor(output_buffer, layout)

    # Initialize input buffers with sample data
    var message_bytes: List[UInt8] = [
        71, 100, 107, 107, 110, 31, 76, 110, 105, 110
    ]
    with left_buffer.map_to_host() as mapped_buffer:
        var mapped_tensor = TileTensor(mapped_buffer, layout)
        for idx in range(VECTOR_WIDTH):
            mapped_tensor[idx] = message_bytes[idx]
    _ = right_buffer.enqueue_fill(1)

    # Launch GPU kernel
    ctx.enqueue_function[vector_addition](https://mojolang.org/docs/tools/left_tensor,
        right_tensor,
        output_tensor,
        grid_dim=1,
        block_dim=VECTOR_WIDTH,.md)
    ctx.synchronize()

    # Read results back and print as ASCII
    with output_buffer.map_to_host() as mapped_buffer:
        var mapped_tensor = TileTensor(mapped_buffer, layout)
        for idx in range(VECTOR_WIDTH):
            print(chr(Int(mapped_tensor[idx])), end="")
        print()
```

Output:

```output
Hello Mojo
```

:::tip
Learn Mojo GPU programming through the interactive
[Mojo GPU Puzzles](https://puzzles.modular.com/introduction.html).
:::
