A core requirement of WebAssembly was that (ignoring I/O for the moment and considering only the computational core) you should be able to run arbitrary existing code on it, and the effort involved in getting it working should be comparable to porting to a new architecture, not to a new programming language. What this particularly meant, in practice, was that it needed to be a good compilation target for C and C++, since most code is written either in those languages or in interpreted languages whose interpreters are written in those languages. (It also needs to support languages that's not true of, like Go, Rust, and Swift, but once you've got C and C++, those languages don't pose major additional conceptual difficulties.)

The JVM and CLR are poor compilation targets for C and C++, because those languages weren't designed to target those runtimes and those runtimes weren't designed to run those languages. (C++/CLI isn't C++.) It's possible to get something working, and a few people have tried, but you run into a lot of impedance mismatches and compatibility issues. I think you would see people run into a lot more problems trying to get their code running on the JVM or CLR than they in fact run into trying to get it running on WebAssembly. (Though I think the CLR is less bad about this than the JVM.)

As for the idea of using LLVM bitcode as an interchange format, we don't have to guess how that would have gone, because it was actually tried! Google implemented this in Chrome and called it PNaCl, and some sites and extensions relied on it for a while. They ultimately withdrew it in favor of WebAssembly. I don't understand all the reasons why it failed, but I think part of the problem is that it ran into a bunch of "the spec is whatever LLVM happens to do" type issues that were real problems for would-be toolchain authors and made the other browser vendors (including Apple, LLVM's de facto primary corporate sponsor) reluctant to support it. WebAssembly has a relatively short and simple standard that you can actually read; writing a WebAssembly interpreter is an undergraduate exercise, though of course writing a highly performant one is much more work.

Also, as far as I can tell, LLVM hasn't at all been optimized to death for the use case of runtime code generation, where the speed of the compiler is about as important as that of the generated code. The biggest dynamic language I know that uses LLVM is Julia, which is a decently big deal, but the overwhelming majority of LLVM usage is for ahead-of-time compilation of languages like C, C++, Swift, and Rust.

On a bigger-picture note, I'm not sure I at all understand why adopting an existing bytecode language would have made things easier. Yes, it would have been much easier to reuse existing Java code if the JVM had been adopted, or to reuse existing C# code if the CLR had been adopted, but those options are mutually exclusive; the goal was something that would work at least okay for all the languages. Python doesn't have a stable bytecode format, and Rust and Haskell compile to LLVM bitcode (which LLVM has no problem lowering to WebAssembly since WebAssembly was designed to make that straightforward), so I don't see how those languages are in any way disadvantaged by the choice of WebAssembly as the target bytecode language instead of some alternative.

Or are your concerns about I/O? That's a bigger can of worms, and you'd need to explain how you imagine this would work, but the short version is that reusing the interfaces that existing OSes provided would not have worked well, because the browser has a different (and in many ways better) security model.