diff --git a/CHANGELOG.md b/CHANGELOG.md
index bc210a71..0d38cbd3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@
   - `#wait` wait for file download to be completed
   - `#set_behavior` where and whether to store file
 - `Browser::Client#command` accepts :async parameter [#433]
+- `Ferrum::Browser` introduce `:flatten` mode with one connection and sessions
 
 ### Changed
 - `Ferrum::Page#screeshot` accepts :area option [#410]
@@ -533,9 +534,8 @@ to `Ferrum::Browser#default_context`
 ### Fixed
 
 ### Removed
-- `Ferrum::EmptyTargetsError`
-- the `hack` to handle `new window` which doesn't have events at all by `Ferrum::Page#session_id` with
-`Target.attachToTarget` and `Target.detachFromTarget` usage
+- `Ferrum::EmptyTargetsError` the hack to handle `new window` which doesn't have events at all by
+`Ferrum::Page#session_id` with `Target.attachToTarget` and `Target.detachFromTarget` usage
 - `Ferrum::Page#close_connection` - the logic is moved to `Ferrum::Page#close` directly
 - the third argument (`new_window = false`) for `Ferrum::Page` initializer
 - `Ferrum::Targets` class with the delegations to `Ferrum::Targets` instance in `Ferrum::Browser` instance:
diff --git a/lib/ferrum/browser.rb b/lib/ferrum/browser.rb
index e1186472..17fb6057 100644
--- a/lib/ferrum/browser.rb
+++ b/lib/ferrum/browser.rb
@@ -47,6 +47,9 @@ class Browser
     # @option options [Boolean] :xvfb (false)
     #   Run browser in a virtual framebuffer.
     #
+    # @option options [Boolean] :flatten (true)
+    #   Use one websocket connection to the browser and all the pages in flatten mode.
+    #
     # @option options [(Integer, Integer)] :window_size ([1024, 768])
     #   The dimensions of the browser window in which to test, expressed as a
     #   2-element array, e.g. `[1024, 768]`.
diff --git a/lib/ferrum/browser/options.rb b/lib/ferrum/browser/options.rb
index 6f1c9acd..06a7cc17 100644
--- a/lib/ferrum/browser/options.rb
+++ b/lib/ferrum/browser/options.rb
@@ -15,7 +15,7 @@ class Options
                   :js_errors, :base_url, :slowmo, :pending_connection_errors,
                   :url, :env, :process_timeout, :browser_name, :browser_path,
                   :save_path, :proxy, :port, :host, :headless, :browser_options,
-                  :ignore_default_browser_options, :xvfb
+                  :ignore_default_browser_options, :xvfb, :flatten
       attr_accessor :timeout, :ws_url, :default_user_agent
 
       def initialize(options = nil)
@@ -27,6 +27,7 @@ def initialize(options = nil)
         @window_size = @options.fetch(:window_size, WINDOW_SIZE)
         @js_errors = @options.fetch(:js_errors, false)
         @headless = @options.fetch(:headless, true)
+        @flatten = @options.fetch(:flatten, true)
         @pending_connection_errors = @options.fetch(:pending_connection_errors, true)
         @process_timeout = @options.fetch(:process_timeout, PROCESS_TIMEOUT)
         @slowmo = @options[:slowmo].to_f
diff --git a/lib/ferrum/client.rb b/lib/ferrum/client.rb
index c909973b..80856e9a 100644
--- a/lib/ferrum/client.rb
+++ b/lib/ferrum/client.rb
@@ -5,11 +5,59 @@
 require "ferrum/client/web_socket"
 
 module Ferrum
+  class SessionClient
+    attr_reader :client, :session_id
+
+    def self.event_name(event, session_id)
+      [event, session_id].compact.join("_")
+    end
+
+    def initialize(client, session_id)
+      @client = client
+      @session_id = session_id
+    end
+
+    def command(method, async: false, **params)
+      message = build_message(method, params)
+      @client.send_message(message, async: async)
+    end
+
+    def on(event, &block)
+      @client.on(event_name(event), &block)
+    end
+
+    def subscribed?(event)
+      @client.subscribed?(event_name(event))
+    end
+
+    def respond_to_missing?(name, include_private)
+      @client.respond_to?(name, include_private)
+    end
+
+    def method_missing(name, ...)
+      @client.send(name, ...)
+    end
+
+    def close
+      @client.subscriber.clear(session_id: session_id)
+    end
+
+    private
+
+    def build_message(method, params)
+      @client.build_message(method, params).merge(sessionId: session_id)
+    end
+
+    def event_name(event)
+      self.class.event_name(event, session_id)
+    end
+  end
+
   class Client
     extend Forwardable
     delegate %i[timeout timeout=] => :options
 
-    attr_reader :options
+    attr_reader :options, :subscriber
 
     def initialize(ws_url, options)
       @command_id = 0
@@ -54,6 +102,10 @@ def subscribed?(event)
       @subscriber.subscribed?(event)
     end
 
+    def session(session_id)
+      SessionClient.new(self, session_id)
+    end
+
     def close
       @ws.close
       # Give a thread some time to handle a tail of messages
diff --git a/lib/ferrum/client/subscriber.rb b/lib/ferrum/client/subscriber.rb
index 7fc52a64..74b8887c 100644
--- a/lib/ferrum/client/subscriber.rb
+++ b/lib/ferrum/client/subscriber.rb
@@ -35,6 +35,10 @@ def close
         @priority_thread&.kill
       end
 
+      def clear(session_id:)
+        @on.delete_if { |k, _| k.match?(session_id) }
+      end
+
       private
 
       def start
@@ -58,9 +62,11 @@ def start
       end
 
       def call(message)
-        method, params = message.values_at("method", "params")
-        total = @on[method].size
-        @on[method].each_with_index do |block, index|
+        method, session_id, params = message.values_at("method", "sessionId", "params")
+        event = SessionClient.event_name(method, session_id)
+
+        total = @on[event].size
+        @on[event].each_with_index do |block, index|
           # In case of multiple callbacks we provide current index and total
           block.call(params, index, total)
         end
diff --git a/lib/ferrum/context.rb b/lib/ferrum/context.rb
index e0628017..af71f8d2 100644
--- a/lib/ferrum/context.rb
+++ b/lib/ferrum/context.rb
@@ -53,8 +53,8 @@ def create_target
       target
     end
 
-    def add_target(params)
-      new_target = Target.new(@client, params)
+    def add_target(params:, session_id: nil)
+      new_target = Target.new(@client, session_id, params)
       target = @targets.put_if_absent(new_target.id, new_target)
       target ||= new_target # `put_if_absent` returns nil if added a new value or existing if there was one already
       @pendings.put(target, @client.timeout) if @pendings.empty?
@@ -71,7 +71,7 @@ def delete_target(target_id)
 
     def close_targets_connection
       @targets.each_value do |target|
-        next unless target.attached?
+        next unless target.connected?
 
         target.page.close_connection
       end
diff --git a/lib/ferrum/contexts.rb b/lib/ferrum/contexts.rb
index 9753c0e7..1a0f1ddf 100644
--- a/lib/ferrum/contexts.rb
+++ b/lib/ferrum/contexts.rb
@@ -12,6 +12,7 @@ def initialize(client)
       @contexts = Concurrent::Map.new
       @client = client
       subscribe
+      auto_attach
       discover
     end
 
@@ -67,12 +68,23 @@ def size
     private
 
     def subscribe
+      @client.on("Target.attachedToTarget") do |params|
+        info, session_id = params.values_at("targetInfo", "sessionId")
+        next unless info["type"] == "page"
+
+        context_id = info["browserContextId"]
+        @contexts[context_id]&.add_target(session_id: session_id, params: info)
+        if params["waitingForDebugger"]
+          @client.session(session_id).command("Runtime.runIfWaitingForDebugger", async: true)
+        end
+      end
+
       @client.on("Target.targetCreated") do |params|
         info = params["targetInfo"]
         next unless info["type"] == "page"
 
         context_id = info["browserContextId"]
-        @contexts[context_id]&.add_target(info)
+        @contexts[context_id]&.add_target(params: info)
       end
 
       @client.on("Target.targetInfoChanged") do |params|
@@ -97,5 +109,11 @@ def subscribe
     def discover
       @client.command("Target.setDiscoverTargets", discover: true)
     end
+
+    def auto_attach
+      return unless @client.options.flatten
+
+      @client.command("Target.setAutoAttach", autoAttach: true, waitForDebuggerOnStart: true, flatten: true)
+    end
   end
 end
diff --git a/lib/ferrum/target.rb b/lib/ferrum/target.rb
index 8dfc8af2..099851c9 100644
--- a/lib/ferrum/target.rb
+++ b/lib/ferrum/target.rb
@@ -8,17 +8,20 @@ class Target
     # where we enhance page class and build page ourselves.
     attr_writer :page
 
-    def initialize(client, params = nil)
+    attr_reader :session_id
+
+    def initialize(client, session_id = nil, params = nil)
       @page = nil
       @client = client
+      @session_id = session_id
       @params = params
     end
 
     def update(params)
-      @params = params
+      @params.merge!(params)
     end
 
-    def attached?
+    def connected?
       !!@page
     end
 
@@ -68,6 +71,8 @@ def maybe_sleep_if_new_window
 
     def build_client
       options = @client.options
+      return @client.session(session_id) if options.flatten
+
       ws_url = options.ws_url.merge(path: "/devtools/page/#{id}").to_s
       Client.new(ws_url, options)
     end