diff --git a/moonbit-docs/docs/examples/pingpong/index.md b/moonbit-docs/docs/examples/pingpong/index.md
new file mode 100644
index 00000000..be2e4710
--- /dev/null
+++ b/moonbit-docs/docs/examples/pingpong/index.md
@@ -0,0 +1 @@
+# PingPong
diff --git a/moonbit-docs/i18n/zh/docusaurus-plugin-content-docs/current/examples/pingpong/index.md b/moonbit-docs/i18n/zh/docusaurus-plugin-content-docs/current/examples/pingpong/index.md
new file mode 100644
index 00000000..681462c3
--- /dev/null
+++ b/moonbit-docs/i18n/zh/docusaurus-plugin-content-docs/current/examples/pingpong/index.md
@@ -0,0 +1,294 @@
+# Wasm4 案例:双人乒乓球
+
+WASM-4 是一款使用 WebAssembly 实现的较底层的虚拟游戏机,主要用于构建小型、复古的游戏。 和其他一些可能可以编译到 WebAssembly 的语言不同,MoonBit 为 WASM 平台提供**第一方支持**,社区则提供了 WASM-4 的 binding。 使用 WASM-4 进行游戏开发,MoonBit 是有许多优势的。
+
+我们用一个十分简单的,但却支持**多人游玩**的乒乓游戏来展示如何利用 WASM-4 开发小型游戏。
+
+![ping pong](ping-pong.png)
+
+## 配置环境
+
+- wasm-4 runtime,可通过 npm 安装:
+
+```bash
+npm install -D wasm4
+```
+
+- 新建一个 MoonBit 项目,将 wasm4 binding 作为依赖加入到项目中: `moon add moonbitlang/wasm4`.
+
+- 修改 `moon.pkg.json` ,linking 配置中应当导出 `start` `update` 方法供 wasm-4 调用::
+
+```json
+{
+ "import": ["moonbitlang/wasm4"],
+ "link": {
+ "wasm-gc": {
+ "exports": ["start", "update"],
+ "import-memory": {
+ "module": "env",
+ "name": "memory"
+ }
+ },
+ "wasm": {
+ "exports": ["start", "update"],
+ "import-memory": {
+ "module": "env",
+ "name": "memory"
+ },
+ "heap-start-address": 6590
+ }
+ }
+}
+```
+
+注意对于 wasm 后端,在 `0x0000 ~ 0x19BE`(`6590`,开区间)范围内的地址是分配给 wasm4 ABI 的,MoonBit 的堆在该区间之后。 若使用 wasm-gc 后端则不需配置。
+
+- 根据 ABI 的规格,必须要导出 `start` `update` 方法
+ - `start` 仅在游戏初始化时执行一次
+ - `update` 会以游戏刷新率(60Hz)的频率执行
+
+基本想法是,我们将任何静态代码(即那些不会改变游戏状态的代码)放在 `start` 中(例如初始化调色板); 除此之外的动态代码(即那些会更改游戏状态的),就需要放在 `update` 中。 游戏的主要逻辑和相关的函数调用都是在 `update` 中完成的。
+
+在 MoonBit 中,函数是通过改变其可见性为 `pub` 来实现导出功能的。
+
+确保环境配置一切无误之后,我们就可以开始编写游戏了。
+
+## 画一个乒乓球
+
+毕竟我们是实现一个乒乓游戏,那么最基本的工作就是绘制球和挡板:
+
+```moonbit
+@wasm4.set_draw_colors(0x2U, index=1)
+@wasm4.set_draw_colors(0x3U, index=2)
+@wasm4.oval(bs.ball_x, bs.ball_y, ball_size, ball_size)
+@wasm4.rect(0, bs.y_2, width, height)
+@wasm4.rect(screen_size - width, bs.y_1, width, height)
+```
+
+`oval` 用于在给定中心 `(ball_x,ball_y)` 处绘制椭圆。长轴和短轴相等的椭圆就是一个圆,且它的直径是 `ball_size`。
+
+### 为元素上色
+
+根据 [memory layout](https://wasm4.org/docs/reference/memory), wasm-4 的调色板寄存器( `PALETTE`,在 MoonBit 的绑定中对用户不可见)一次只能存储 4 种颜色, 但可以通过随时更改这一寄存器来引入新的颜色。我们使用 `set_palette` 来配置调色板,但在这个实现中就使用默认颜色配置了。
+
+默认的配色看起来像 gameboy 的:
+
+
+
Color 1
+
Color 2
+
Color 3
+
Color 4
+
+
+但需要注意内置的绘图函数不直接访问这个寄存器, 而是访问同样能够存储 4 个颜色的 `DRAW_COLORS` 寄存器(也对用户不可见)。
+
+```moonbit
+// 按 `palette_index` 访问 PALETTE 的颜色
+// 并将之设定为 `DRAW_COLORS` 对应 `index` 的颜色
+set_draw_colors(palette_index, index)
+```
+
+这两个寄存器的关键区别在于 `DRAW_COLORS` 的 4 个颜色具有不同的用途:
+
+对内置的 `oval` `rectangle`形状来说,寄存器的第一个颜色是填充色,第二个颜色是边框色。
+
+因而我们的乒乓球看起来应该像:
+
+
+
+注意像这样动态的绘图代码必须在 `update`中调用。更新游戏状态的唯一方法就是通过 `update` 函数。
+
+## 处理撞击情况
+
+在 wasm-4 中,原点 `(0,0)` 在左上角,纵轴指向下。
+
+乒乓球的撞击情况有三种:
+
+- 球根本没有发生撞击,记为 `0`
+- 球与左侧挡板撞击,弹向右侧,记为 `1`
+- 球与右侧挡板撞击,弹向左侧,记为 `-1`
+
+我们将左右侧挡板记为 `y_1` `y_2` (挡板只能上下移动)。用于处理撞击的函数必须返回球撞击后的方向(或0),即 `0/1/-1`.
+
+列出这些情况后,我们可以实现 `paddle_collision` 如下:
+
+```moonbit
+pub fn paddle_collision(
+ y_1 : Int,
+ y_2 : Int,
+ ball_x : Int,
+ ball_y : Int
+) -> Int {
+ if ball_x < width && ball_y < y_2 + height && ball_y + ball_size > y_2 {
+ 1
+ } else if ball_x + ball_size > screen_size - width &&
+ ball_y < y_1 + height &&
+ ball_y + ball_size > y_1 {
+ -1
+ } else {
+ 0
+ }
+}
+```
+
+## 移动挡板
+
+移动挡板就是更新 `y_1` `y_2`。在 wasm-4 中,用户输入是通过 gamepads 获取的。一个 gamepad 包括 4 个方向键,两个动作键。总共有 4 个 gamepads(即最多 4 名玩家),默认情况下 `gamepad1` 最先使用。
+
+MoonBit 中使用以下方法访问 gamepad:
+
+```moonbit
+get_gamepad().