diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,11 @@
 
 # Unreleased
 
+# 0.2.3.1
+
+* Binary fetching: don't create the toolsRoot directory unless necessary.
+* Binary fetching: use logging instead of stdout/stderr so it doesn't mess up the TUI interface.
+
 # 0.2.3.0
 
 * When autodetecting Chrome, look for `google-chrome-stable` as well as `google-chrome`. (It's found on NixOS.)
diff --git a/sandwich-webdriver.cabal b/sandwich-webdriver.cabal
--- a/sandwich-webdriver.cabal
+++ b/sandwich-webdriver.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           sandwich-webdriver
-version:        0.2.3.0
+version:        0.2.3.1
 synopsis:       Sandwich integration with Selenium WebDriver
 description:    Please see the <https://codedownio.github.io/sandwich/docs/extensions/sandwich-webdriver documentation>.
 category:       Testing
diff --git a/src/Test/Sandwich/WebDriver/Internal/Binaries.hs b/src/Test/Sandwich/WebDriver/Internal/Binaries.hs
--- a/src/Test/Sandwich/WebDriver/Internal/Binaries.hs
+++ b/src/Test/Sandwich/WebDriver/Internal/Binaries.hs
@@ -21,9 +21,11 @@
 import qualified Data.Text as T
 import GHC.Stack
 import System.Directory
+import System.Exit
 import System.FilePath
 import System.IO.Temp
 import System.Process
+import Test.Sandwich.Expectations
 import Test.Sandwich.Logging
 import Test.Sandwich.WebDriver.Internal.Binaries.DetectChrome
 import Test.Sandwich.WebDriver.Internal.Binaries.DetectFirefox
@@ -42,23 +44,24 @@
 
 -- * Obtaining binaries
 
+defaultSeleniumJarUrl :: String
+defaultSeleniumJarUrl = "https://selenium-release.storage.googleapis.com/3.141/selenium-server-standalone-3.141.59.jar"
+
 -- TODO: remove curl dependencies here
 
 -- | Manually obtain a Selenium server JAR file, according to the 'SeleniumToUse' policy,
 -- storing it under the provided 'FilePath' if necessary and returning the exact path.
-obtainSelenium :: (MonadIO m, MonadLogger m) => FilePath -> SeleniumToUse -> m (Either T.Text FilePath)
+obtainSelenium :: (MonadIO m, MonadLogger m, MonadBaseControl IO m, MonadThrow m) => FilePath -> SeleniumToUse -> m (Either T.Text FilePath)
 obtainSelenium toolsDir (DownloadSeleniumFrom url) = do
-  let seleniumPath = [i|#{toolsDir}/selenium-server-standalone.jar|]
-  liftIO $ createDirectoryIfMissing True (takeDirectory seleniumPath)
-  unlessM (liftIO $ doesFileExist seleniumPath) $
-    void $ liftIO $ readCreateProcess (shell [i|curl #{url} -o #{seleniumPath}|]) ""
-  return $ Right seleniumPath
+  let path = [i|#{toolsDir}/selenium-server-standalone.jar|]
+  unlessM (liftIO $ doesFileExist path) $
+    curlDownloadToPath url path
+  return $ Right path
 obtainSelenium toolsDir DownloadSeleniumDefault = do
-  let seleniumPath = [i|#{toolsDir}/selenium-server-standalone-3.141.59.jar|]
-  liftIO $ createDirectoryIfMissing True (takeDirectory seleniumPath)
-  unlessM (liftIO $ doesFileExist seleniumPath) $
-    void $ liftIO $ readCreateProcess (shell [i|curl https://selenium-release.storage.googleapis.com/3.141/selenium-server-standalone-3.141.59.jar -o #{seleniumPath}|]) ""
-  return $ Right seleniumPath
+  let path = [i|#{toolsDir}/selenium-server-standalone-3.141.59.jar|]
+  unlessM (liftIO $ doesFileExist path) $
+    curlDownloadToPath defaultSeleniumJarUrl path
+  return $ Right path
 obtainSelenium _ (UseSeleniumAt path) = liftIO (doesFileExist path) >>= \case
   False -> return $ Left [i|Path '#{path}' didn't exist|]
   True -> return $ Right path
@@ -70,13 +73,12 @@
   ) => FilePath -> ChromeDriverToUse -> m (Either T.Text FilePath)
 obtainChromeDriver toolsDir (DownloadChromeDriverFrom url) = do
   let path = [i|#{toolsDir}/#{chromeDriverExecutable}|]
-  liftIO $ createDirectoryIfMissing True (takeDirectory path)
   unlessM (liftIO $ doesFileExist path) $
-    void $ liftIO $ readCreateProcess (shell [i|curl #{url} -o #{path}|]) ""
+    curlDownloadToPath url path
   return $ Right path
 obtainChromeDriver toolsDir (DownloadChromeDriverVersion chromeDriverVersion) = runExceptT $ do
   let path = getChromeDriverPath toolsDir chromeDriverVersion
-  (liftIO $ doesFileExist path) >>= \case
+  liftIO (doesFileExist path) >>= \case
     True -> return path
     False -> do
       let downloadPath = getChromeDriverDownloadUrl chromeDriverVersion detectPlatform
@@ -91,16 +93,15 @@
 
 -- | Manually obtain a geckodriver binary, according to the 'GeckoDriverToUse' policy,
 -- storing it under the provided 'FilePath' if necessary and returning the exact path.
-obtainGeckoDriver :: (MonadIO m, MonadLogger m, MonadBaseControl IO m) => FilePath -> GeckoDriverToUse -> m (Either T.Text FilePath)
+obtainGeckoDriver :: (MonadIO m, MonadLogger m, MonadBaseControl IO m, MonadThrow m) => FilePath -> GeckoDriverToUse -> m (Either T.Text FilePath)
 obtainGeckoDriver toolsDir (DownloadGeckoDriverFrom url) = do
   let path = [i|#{toolsDir}/#{geckoDriverExecutable}|]
-  liftIO $ createDirectoryIfMissing True (takeDirectory path)
   unlessM (liftIO $ doesFileExist path) $
-    void $ liftIO $ readCreateProcess (shell [i|curl #{url} -o #{path}|]) ""
+    curlDownloadToPath url path
   return $ Right path
 obtainGeckoDriver toolsDir (DownloadGeckoDriverVersion geckoDriverVersion) = runExceptT $ do
   let path = getGeckoDriverPath toolsDir geckoDriverVersion
-  (liftIO $ doesFileExist path) >>= \case
+  liftIO (doesFileExist path) >>= \case
     True -> return path
     False -> do
       let downloadPath = getGeckoDriverDownloadUrl geckoDriverVersion detectPlatform
@@ -121,12 +122,11 @@
   let seleniumPath = [i|#{toolsDir}/selenium-server.jar|]
   liftIO (doesFileExist seleniumPath) >>= flip unless (downloadSelenium seleniumPath)
   return seleniumPath
-
-downloadSelenium :: Constraints m => FilePath -> m ()
-downloadSelenium seleniumPath = void $ do
-  info [i|Downloading selenium-server.jar to #{seleniumPath}|]
-  liftIO $ createDirectoryIfMissing True (takeDirectory seleniumPath)
-  liftIO $ readCreateProcess (shell [i|curl https://selenium-release.storage.googleapis.com/3.141/selenium-server-standalone-3.141.59.jar -o #{seleniumPath}|]) ""
+  where
+    downloadSelenium :: Constraints m => FilePath -> m ()
+    downloadSelenium seleniumPath = void $ do
+      info [i|Downloading selenium-server.jar to #{seleniumPath}|]
+      curlDownloadToPath defaultSeleniumJarUrl seleniumPath
 
 downloadChromeDriverIfNecessary' :: Constraints m => FilePath -> ChromeDriverVersion -> m (Either T.Text FilePath)
 downloadChromeDriverIfNecessary' toolsDir chromeDriverVersion = runExceptT $ do
@@ -164,26 +164,37 @@
 downloadAndUnzipToPath downloadPath localPath = leftOnException' $ do
   info [i|Downloading #{downloadPath} to #{localPath}|]
   liftIO $ createDirectoryIfMissing True (takeDirectory localPath)
-  withSystemTempDirectory "sandwich-webdriver-tool-download" $ \dir -> liftIO $ do
-    void $ readCreateProcess ((proc "curl" [T.unpack downloadPath, "-o", "temp.zip"]) { cwd = Just dir }) ""
+  withSystemTempDirectory "sandwich-webdriver-tool-download" $ \dir -> do
+    curlDownloadToPath (T.unpack downloadPath) (dir </> "temp.zip")
 
-    void $ readCreateProcess ((proc "unzip" ["temp.zip", "-d", "unzipped"]) { cwd = Just dir }) ""
+    createProcessWithLogging ((proc "unzip" ["temp.zip", "-d", "unzipped"]) { cwd = Just dir })
+      >>= liftIO . waitForProcess >>= (`shouldBe` ExitSuccess)
     let unzipped = dir </> "unzipped"
 
-    executables <- (filter (/= "") . T.splitOn "\n" . T.pack) <$> readCreateProcess (proc "find" [unzipped, "-executable", "-type", "f"]) ""
+    executables <- (filter (/= "") . T.splitOn "\n" . T.pack) <$> readCreateProcessWithLogging (proc "find" [unzipped, "-executable", "-type", "f"]) ""
     case executables of
-      [] -> throwIO $ userError [i|No executable found in file downloaded from #{downloadPath}|]
+      [] -> liftIO $ throwIO $ userError [i|No executable found in file downloaded from #{downloadPath}|]
       [x] -> do
-        copyFile (T.unpack x) localPath
-        liftIO $ void $ readCreateProcess (shell [i|chmod u+x #{localPath}|]) ""
-      xs -> throwIO $ userError [i|Found multiple executable found in file downloaded from #{downloadPath}: #{xs}|]
+        liftIO $ copyFile (T.unpack x) localPath
+        createProcessWithLogging (shell [i|chmod u+x #{localPath}|])
+          >>= liftIO . waitForProcess >>= (`shouldBe` ExitSuccess)
+      xs -> liftIO $ throwIO $ userError [i|Found multiple executable found in file downloaded from #{downloadPath}: #{xs}|]
 
-downloadAndUntarballToPath :: (MonadIO m, MonadBaseControl IO m, MonadLogger m) => T.Text -> FilePath -> m (Either T.Text ())
+downloadAndUntarballToPath :: (MonadIO m, MonadBaseControl IO m, MonadLogger m, MonadThrow m) => T.Text -> FilePath -> m (Either T.Text ())
 downloadAndUntarballToPath downloadPath localPath = leftOnException' $ do
   info [i|Downloading #{downloadPath} to #{localPath}|]
   liftIO $ createDirectoryIfMissing True (takeDirectory localPath)
-  liftIO $ void $ readCreateProcess (shell [i|wget -qO- #{downloadPath} | tar xvz  -C #{takeDirectory localPath}|]) ""
-  liftIO $ void $ readCreateProcess (shell [i|chmod u+x #{localPath}|]) ""
+  createProcessWithLogging (shell [i|wget -qO- #{downloadPath} | tar xvz  -C #{takeDirectory localPath}|])
+    >>= liftIO . waitForProcess >>= (`shouldBe` ExitSuccess)
+  createProcessWithLogging (shell [i|chmod u+x #{localPath}|])
+    >>= liftIO . waitForProcess >>= (`shouldBe` ExitSuccess)
+
+curlDownloadToPath :: (MonadIO m, MonadLogger m, MonadBaseControl IO m, MonadThrow m) => String -> FilePath -> m ()
+curlDownloadToPath downloadPath localPath = do
+  info [i|Downloading #{downloadPath} to #{localPath}|]
+  liftIO $ createDirectoryIfMissing True (takeDirectory localPath)
+  p <- createProcessWithLogging (proc "curl" [downloadPath, "-o", localPath, "-s"])
+  liftIO (waitForProcess p) >>= (`shouldBe` ExitSuccess)
 
 unlessM :: Monad m => m Bool -> m () -> m ()
 unlessM b s = b >>= (\t -> unless t s)
diff --git a/src/Test/Sandwich/WebDriver/Internal/StartWebDriver.hs b/src/Test/Sandwich/WebDriver/Internal/StartWebDriver.hs
--- a/src/Test/Sandwich/WebDriver/Internal/StartWebDriver.hs
+++ b/src/Test/Sandwich/WebDriver/Internal/StartWebDriver.hs
@@ -73,7 +73,6 @@
 
   -- Get selenium and chromedriver
   debug [i|Preparing to create the Selenium process|]
-  liftIO $ createDirectoryIfMissing True toolsRoot
   seleniumPath <- obtainSelenium toolsRoot seleniumToUse >>= \case
     Left err -> error [i|Failed to obtain selenium: '#{err}'|]
     Right p -> return p
