diff --git a/Makefile b/Makefile
index e2aef5f8..e9fd7c50 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@ SWIFT_SDK_ID ?= wasm32-unknown-wasi
 .PHONY: bootstrap
 bootstrap:
 	npm ci
-	npx playwright install
+	npx playwright install chromium-headless-shell
 
 .PHONY: unittest
 unittest:
diff --git a/Sources/JavaScriptEventLoop/WebWorkerDedicatedExecutor.swift b/Sources/JavaScriptEventLoop/WebWorkerDedicatedExecutor.swift
index d42c5add..28bc4545 100644
--- a/Sources/JavaScriptEventLoop/WebWorkerDedicatedExecutor.swift
+++ b/Sources/JavaScriptEventLoop/WebWorkerDedicatedExecutor.swift
@@ -39,12 +39,19 @@ public final class WebWorkerDedicatedExecutor: SerialExecutor {
     private let underlying: WebWorkerTaskExecutor
 
     /// - Parameters:
+    ///   - stackSize: The stack size for each worker thread. Default is `nil` (use the platform default stack size).
     ///   - timeout: The maximum time to wait for all worker threads to be started. Default is 3 seconds.
     ///   - checkInterval: The interval to check if all worker threads are started. Default is 5 microseconds.
     /// - Throws: An error if any worker thread fails to initialize within the timeout period.
-    public init(timeout: Duration = .seconds(3), checkInterval: Duration = .microseconds(5)) async throws {
+    /// - Note: The default stack size of wasi-libc is typically 128KB.
+    public init(
+        stackSize: Int? = nil,
+        timeout: Duration = .seconds(3),
+        checkInterval: Duration = .microseconds(5)
+    ) async throws {
         let underlying = try await WebWorkerTaskExecutor(
             numberOfThreads: 1,
+            stackSize: stackSize,
             timeout: timeout,
             checkInterval: checkInterval
         )
diff --git a/Sources/JavaScriptEventLoop/WebWorkerTaskExecutor.swift b/Sources/JavaScriptEventLoop/WebWorkerTaskExecutor.swift
index 651e7be2..f3923117 100644
--- a/Sources/JavaScriptEventLoop/WebWorkerTaskExecutor.swift
+++ b/Sources/JavaScriptEventLoop/WebWorkerTaskExecutor.swift
@@ -347,7 +347,7 @@ public final class WebWorkerTaskExecutor: TaskExecutor {
             self.workers = workers
         }
 
-        func start(timeout: Duration, checkInterval: Duration) async throws {
+        func start(stackSize: Int?, timeout: Duration, checkInterval: Duration) async throws {
             #if canImport(wasi_pthread) && compiler(>=6.1) && _runtime(_multithreaded)
             class Context: @unchecked Sendable {
                 let executor: WebWorkerTaskExecutor.Executor
@@ -375,9 +375,21 @@ public final class WebWorkerTaskExecutor: TaskExecutor {
                 let unmanagedContext = Unmanaged.passRetained(context)
                 contexts.append(unmanagedContext)
                 let ptr = unmanagedContext.toOpaque()
+                var attr = pthread_attr_t()
+                pthread_attr_init(&attr)
+                // Set the stack size if specified.
+                if let stackSize {
+                    let ret = pthread_attr_setstacksize(&attr, stackSize)
+                    guard ret == 0 else {
+                        let strerror = String(cString: strerror(ret))
+                        throw SpawnError(
+                            reason: "Failed to set stack size (\(stackSize)) for thread (\(ret): \(strerror))"
+                        )
+                    }
+                }
                 let ret = pthread_create(
                     nil,
-                    nil,
+                    &attr,
                     { ptr in
                         // Cast to a optional pointer to absorb nullability variations between platforms.
                         let ptr: UnsafeMutableRawPointer? = ptr
@@ -390,6 +402,7 @@ public final class WebWorkerTaskExecutor: TaskExecutor {
                     },
                     ptr
                 )
+                pthread_attr_destroy(&attr)
                 guard ret == 0 else {
                     let strerror = String(cString: strerror(ret))
                     throw SpawnError(reason: "Failed to create a thread (\(ret): \(strerror))")
@@ -467,16 +480,19 @@ public final class WebWorkerTaskExecutor: TaskExecutor {
     ///
     /// - Parameters:
     ///   - numberOfThreads: The number of Web Worker threads to spawn.
+    ///   - stackSize: The stack size for each worker thread. Default is `nil` (use the platform default stack size).
     ///   - timeout: The maximum time to wait for all worker threads to be started. Default is 3 seconds.
     ///   - checkInterval: The interval to check if all worker threads are started. Default is 5 microseconds.
     /// - Throws: An error if any worker thread fails to initialize within the timeout period.
+    /// - Note: The default stack size of wasi-libc is typically 128KB.
     public init(
         numberOfThreads: Int,
+        stackSize: Int? = nil,
         timeout: Duration = .seconds(3),
         checkInterval: Duration = .microseconds(5)
     ) async throws {
         self.executor = Executor(numberOfThreads: numberOfThreads)
-        try await self.executor.start(timeout: timeout, checkInterval: checkInterval)
+        try await self.executor.start(stackSize: stackSize, timeout: timeout, checkInterval: checkInterval)
     }
 
     /// Terminates all worker threads managed by this executor.
diff --git a/Tests/JavaScriptEventLoopTests/WebWorkerTaskExecutorTests.swift b/Tests/JavaScriptEventLoopTests/WebWorkerTaskExecutorTests.swift
index b9c42c02..46afc9c8 100644
--- a/Tests/JavaScriptEventLoopTests/WebWorkerTaskExecutorTests.swift
+++ b/Tests/JavaScriptEventLoopTests/WebWorkerTaskExecutorTests.swift
@@ -122,6 +122,12 @@ final class WebWorkerTaskExecutorTests: XCTestCase {
         executor.terminate()
     }
 
+    func testThreadStackSize() async throws {
+        // Sanity check for stackSize parameter
+        let executor = try await WebWorkerTaskExecutor(numberOfThreads: 3, stackSize: 512 * 1024)
+        executor.terminate()
+    }
+
     func testTaskGroupRunOnDifferentThreads() async throws {
         let executor = try await WebWorkerTaskExecutor(numberOfThreads: 2)