diff --git a/conn_test.go b/conn_test.go
index 7514540dfdb8bd66e84ee0eac33edbde68eaf627..68dc837da96639868314eee78dbfb16d242ea886 100644
--- a/conn_test.go
+++ b/conn_test.go
@@ -271,12 +271,11 @@ func TestWasm(t *testing.T) {
 		t.Skip("skipping on CI")
 	}
 
-	var g websocket.Grace
-	defer g.Close()
-	s := httptest.NewServer(g.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+	// TODO grace
+	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		c, err := websocket.Accept(w, r, &websocket.AcceptOptions{
-			Subprotocols:       []string{"echo"},
-			InsecureSkipVerify: true,
+			Subprotocols:   []string{"echo"},
+			OriginPatterns: []string{"*"},
 		})
 		if err != nil {
 			t.Errorf("echo server failed: %v", err)
@@ -291,7 +290,7 @@ func TestWasm(t *testing.T) {
 			t.Errorf("echo server failed: %v", err)
 			return
 		}
-	})))
+	}))
 	defer s.Close()
 
 	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
diff --git a/examples/chat/chat_test.go b/examples/chat/chat_test.go
index 2cbc995eb54abe08ed815ae4c3f03f071eb4a2e7..79523d2a81dabc52782e642d8d67fc3ab1547b0a 100644
--- a/examples/chat/chat_test.go
+++ b/examples/chat/chat_test.go
@@ -130,11 +130,10 @@ func setupTest(t *testing.T) (url string, closeFn func()) {
 	cs.subscriberMessageBuffer = 4096
 	cs.publishLimiter.SetLimit(rate.Inf)
 
-	var g websocket.Grace
-	s := httptest.NewServer(g.Handler(cs))
+	// TODO grace
+	s := httptest.NewServer(cs)
 	return s.URL, func() {
 		s.Close()
-		g.Close()
 	}
 }
 
diff --git a/examples/chat/main.go b/examples/chat/main.go
index 1b6f3266cab9d37d320f74a62572830c29bbd47b..cc2d01e8d37a7e51ea22b45d18e06069957ac06c 100644
--- a/examples/chat/main.go
+++ b/examples/chat/main.go
@@ -9,8 +9,6 @@ import (
 	"os"
 	"os/signal"
 	"time"
-
-	"nhooyr.io/websocket"
 )
 
 func main() {
@@ -36,9 +34,9 @@ func run() error {
 	log.Printf("listening on http://%v", l.Addr())
 
 	cs := newChatServer()
-	var g websocket.Grace
+	// TODO grace
 	s := http.Server{
-		Handler:      g.Handler(cs),
+		Handler:      cs,
 		ReadTimeout:  time.Second * 10,
 		WriteTimeout: time.Second * 10,
 	}
@@ -59,8 +57,5 @@ func run() error {
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
 	defer cancel()
 
-	s.Shutdown(ctx)
-	g.Shutdown(ctx)
-
-	return nil
+	return s.Shutdown(ctx)
 }
diff --git a/examples/echo/echo.go b/examples/echo/main.go
similarity index 96%
rename from examples/echo/echo.go
rename to examples/echo/main.go
index 0f31235df93576b0568b46fea90462d3f9ae8fc0..db2d06c9adfd0741b9e5d378f15a3cf27d13bb6a 100644
--- a/examples/echo/echo.go
+++ b/examples/echo/main.go
@@ -24,7 +24,7 @@ import (
 // This example starts a WebSocket echo server,
 // dials the server and then sends 5 different messages
 // and prints out the server's responses.
-func Example_echo() {
+func main() {
 	// First we listen on port 0 which means the OS will
 	// assign us a random free port. This is the listener
 	// the server will serve on and the client will connect to.
@@ -34,15 +34,14 @@ func Example_echo() {
 	}
 	defer l.Close()
 
-	var g websocket.Grace
-	defer g.Close()
+	// TODO grace
 	s := &http.Server{
-		Handler: g.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 			err := echoServer(w, r)
 			if err != nil {
 				log.Printf("echo server: %v", err)
 			}
-		})),
+		}),
 		ReadTimeout:  time.Second * 15,
 		WriteTimeout: time.Second * 15,
 	}
@@ -61,6 +60,7 @@ func Example_echo() {
 	if err != nil {
 		log.Fatalf("client failed: %v", err)
 	}
+
 	// Output:
 	// received: map[i:0]
 	// received: map[i:1]
diff --git a/grace.go b/grace.go
deleted file mode 100644
index a0ec8969bbbc415dec8ae1537644951c2fa3c844..0000000000000000000000000000000000000000
--- a/grace.go
+++ /dev/null
@@ -1,120 +0,0 @@
-package websocket
-
-import (
-	"context"
-	"fmt"
-	"net/http"
-	"sync"
-	"time"
-)
-
-// Grace enables graceful shutdown of accepted WebSocket connections.
-//
-// Use Handler to wrap WebSocket handlers to record accepted connections
-// and then use Close or Shutdown to gracefully close these connections.
-//
-// Grace is intended to be used in harmony with net/http.Server's Shutdown and Close methods.
-// It's required as net/http's Shutdown and Close methods do not keep track of WebSocket
-// connections.
-//
-// Make sure to Close or Shutdown the *http.Server first as you don't want to accept
-// any new connections while the existing websockets are being shut down.
-type Grace struct {
-	handlersMu sync.Mutex
-	closing    bool
-	handlers   map[context.Context]context.CancelFunc
-}
-
-// Handler returns a handler that wraps around h to record
-// all WebSocket connections accepted.
-//
-// Use Close or Shutdown to gracefully close recorded connections.
-// Make sure to Close or Shutdown the *http.Server first.
-func (g *Grace) Handler(h http.Handler) http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		ctx, cancel := context.WithCancel(r.Context())
-		defer cancel()
-
-		r = r.WithContext(ctx)
-
-		ok := g.add(w, ctx, cancel)
-		if !ok {
-			return
-		}
-		defer g.del(ctx)
-
-		h.ServeHTTP(w, r)
-	})
-}
-
-func (g *Grace) add(w http.ResponseWriter, ctx context.Context, cancel context.CancelFunc) bool {
-	g.handlersMu.Lock()
-	defer g.handlersMu.Unlock()
-
-	if g.closing {
-		http.Error(w, "shutting down", http.StatusServiceUnavailable)
-		return false
-	}
-
-	if g.handlers == nil {
-		g.handlers = make(map[context.Context]context.CancelFunc)
-	}
-	g.handlers[ctx] = cancel
-
-	return true
-}
-
-func (g *Grace) del(ctx context.Context) {
-	g.handlersMu.Lock()
-	defer g.handlersMu.Unlock()
-
-	delete(g.handlers, ctx)
-}
-
-// Close prevents the acceptance of new connections with
-// http.StatusServiceUnavailable and closes all accepted
-// connections with StatusGoingAway.
-//
-// Make sure to Close or Shutdown the *http.Server first.
-func (g *Grace) Close() error {
-	g.handlersMu.Lock()
-	for _, cancel := range g.handlers {
-		cancel()
-	}
-	g.handlersMu.Unlock()
-
-	// Wait for all goroutines to exit.
-	g.Shutdown(context.Background())
-
-	return nil
-}
-
-// Shutdown prevents the acceptance of new connections and waits until
-// all connections close. If the context is cancelled before that, it
-// calls Close to close all connections immediately.
-//
-// Make sure to Close or Shutdown the *http.Server first.
-func (g *Grace) Shutdown(ctx context.Context) error {
-	defer g.Close()
-
-	// Same poll period used by net/http.
-	t := time.NewTicker(500 * time.Millisecond)
-	defer t.Stop()
-	for {
-		if g.zeroHandlers() {
-			return nil
-		}
-
-		select {
-		case <-t.C:
-		case <-ctx.Done():
-			return fmt.Errorf("failed to shutdown WebSockets: %w", ctx.Err())
-		}
-	}
-}
-
-func (g *Grace) zeroHandlers() bool {
-	g.handlersMu.Lock()
-	defer g.handlersMu.Unlock()
-	return len(g.handlers) == 0
-}
diff --git a/ws_js.go b/ws_js.go
index 69019e61600635fd829e52a0193df2540d2b5a95..b87e32cdafb2eafa4bccf381a491283e0f96b70e 100644
--- a/ws_js.go
+++ b/ws_js.go
@@ -39,8 +39,6 @@ type Conn struct {
 	readSignal chan struct{}
 	readBufMu  sync.Mutex
 	readBuf    []wsjs.MessageEvent
-
-	g *Grace
 }
 
 func (c *Conn) close(err error, wasClean bool) {