There are several answers for this question.

JavaCaml is a Java package

JavaCaml is entirely written in Java and runs at least on all Java 1.1 platforms. It does not use native code (of course at the cost of speed) and avoids this way incompatibilities between different operating systems. Furthermore, it is not necessary to install a special runtime environment on the target machine, it simply runs out of the box.

JavaCaml has been developed with JDK 1.1.5, but I expect that many Java 1.0 virtual machines can execute the code, too.

JavaCaml interprets bytecode produced by the Objective Caml bytecode compiler

This is the core of JavaCaml: It is a reimplementation of the bytecode interpreter of OCaml. But do not think that it is a good implementation, because it is difficult in Java to imitate Ocaml's runtime environment. The bytecode produced by OCaml is optimized for the execution on a native machine where it is possible to cast types, manipulate addresses and implement stacks in a very efficient way. The Java environment on the contrary has many limitations: no free (typeless) access to memory, no free control of the program flow (no "goto" instruction), no free control of the runtime stack (within a Java method, the stack cannot arbitrarily grow or shrink). So many instructions intended to be efficiently executed using low-level means on a native machine had to be imitated by high-level Java constructs.

An example of this approach is the imitation of values. Although OCaml is a typed language in the bytecode all explicit knowledge about type constraints have been removed (with only a few exceptions). Normally, a native machine does not have the concept of types, and a value is represented by a bit string that has a type only so far as it is accessed by instructions assuming a certain type. For example, the representations of arrays and tuples are absolutely the same, and the only difference is that arrays are accessed using variable indexes while tuple compenents are read or modified using fixed indexes.
Ocaml knows functions with polymorphic type. These functions have input values with unknown type that can be used only in an abstract way, i.e. it is not allowed to access their components. In the bytecode translation such functions must be able to handle arbitrary inputs which might be simple integers as well as references to complex structures. On a native machine these inputs are simply numbers, either meant as numbers or as addresses to memory blocks. In Java it is not possible to cast arbitrarily which means that if we want to represent polymorphic values we must define a class hierarchy modeling the possible value structures. In the result we imitate a simple low-level technique by high-level class hierarchies.

The bytecode language defines a set of instructions and a set of so-called primitives, which are in fact less primitive than the instructions. The primitives are the variable part of the language because it is possible to define additional primitives and access them as normal Ocaml functions. The JavaCaml interpreter currently does neither implement all instructions nor all primitives required for the core OCaml language, but this is only a matter of time. Because of this the JavaCaml interpreter is not fully compatible with the original interpreter, see the list of supported features for details.

JavaCaml is a configurable applet

The JavaCaml interpreter can be simply included on every web page as an applet. The applet has a parameter that determines which Ocaml bytecode file should be executed. This simply means that the behaviour of the applet can be fully controlled by parameters, in terms of theoretical computer science it is a universal machine and the bytecode file is the Goedel number selecting the task to do.

JavaCaml is a client-side scripting environment

In the WWW world the term script mainly refers to programs that take their inputs from HTML forms and produce as output a new HTML page showing its results. On the client-side, JavaScript can do such jobs, but it is a very simple language that is overtaxed when text processing or manipulation of complex structures must be done. In such cases, you can up to now either write expensive Java programs or solve your problem at the server-side where you can freely choose a well-suited programming language.

In Objective Caml you can easily express any structures and the native runtime environment even executes your programs in a highly efficient way (comparable with the speed of languages such as C++). The JavaCaml runtime environment is much slower (a factor from 10 to 100 depending on the problem), but its speed is for typical WWW problems high enough. Its is a comfortable language with features such as automatic memory management, a rich set of data types, powerful notations such as pattern matching, and high-level string functions (regular expressions). Objective Caml can substitute almost any other scripting language.

The JavaCaml runtime environment has some extensions that enables an interaction facility between JavaScript and OCaml programs. This is important since an applet cannot generate HTML pages without the help of JavaScript.