g | x | w | all
Bytes Lang Time Link
114Ruby240529T232819ZLevel Ri
013Vyxal240530T205336Zemanresu
01205AB1E240528T091622ZKevin Cr
078Pyth160909T212058ZSteven H
283Java 7160909T204122ZQBrute
144Ruby160907T000930ZValue In
198Javascript160906T225448ZHedi
189Python 2160906T225718ZJoshua d
126x86 IA32 machine code160907T180733Zanatolyg

Ruby, 114 bytes

->s{t="#{" "*w=2*v=s.size}
"*u=w-1
v.times{|i|9.times{|j|t[[u*v,~v,u][j/3]-[1,~w,w][j%3]*~(j%-4/3*2)*i]=s[~i]}}
t}

Try it online!

Ruby, 120 118 bytes

->s{w=2*v=s.size
t=(" "*w+$/)*w
v.times{|i|9.times{|j|t[[v*w+~v,~v*3,w-2][j/3]-[1,~w,w][j%3]*~(j%-4/3*2)*i]=s[~i]}}
t}

Try it online!

An anonymous function that takes the input string as an argument s and returns a string t.

Explanation

We start by setting up a field of w=2*s.size rows each of w spaces terminated by a newline.

We then substitute, character by character, nine copies of the input string, starting at the endpoint. There are 3 endpoints each with text in 3 different directions x,y,z. This is easier to handle than starting at the beginning, since there are 4 possible startpoints each with text either in 2 or 3 directions. Variable i denotes the distance from the endpoint and s[~i] represents the character in the string, indexed in the reverse direction.

The expressions for the three endpoints are [v*w+~v,~v*3,w-2] (note that ~v*3 is negative - Ruby counts negative numbers backwards from the end of the string starting at -1). The three forward directions (for text on the outside edge of the figure) are [1,~w,w]. w is the diagonal z direction (because each line is newline terminated it has 1+w characters, so w is 1 less than this.) 1 is the x direction and ~w = -(1+w) is the y direction.

Text for edges inside the figure (those that run from the top front right corner) run in the opposite direction. These edges are written when j/3==j%3 or equivalently j%4=0. This is managed by the expression (j%-4/3*2) which evaluates to either 0 or -2. The ~ outside the bracket converts this to -1 or 1. After applying this sign correction we multiply by i to find the right point in the output string.

Commented code

->s{                         #take an input s
  w=2*v=s.size                 #v is the input length. w is double this
  t=(" "*w+$/)*w               #set up a string of w rows of w spaces, newline terminated
  v.times{|i|                  #iterate through characters in s 
    9.times{|j|                  #iterate though edges
      t[[v*w+~v,~v*3,w-2][j/3]-    #pick an end point based on j/3
        [1,~w,w][j%3]*             #subtract direction based on j%3 mutiplied by
        ~(j%-4/3*2)*               #+1 or -1 depending on whether j/3==j%3
        i]=                        #multipy by i
        s[~i]                      #assign character from s to this cell
    }
  }
t}                            #return string t

Vyxal, 13 bytes

L?∞Ṫ»∨¹0²H»ø∧

Try it Online! Indirectly -1 byte thanks to Kevin Cruijssen, I forgot how canvas operator overloading works.

           ø∧ # Draw on the canvas with
L             # Line length: length of input
 ?∞Ṫ          # String: Input, palindromised with last character removed
    »∨¹0²H»   # Directions: digits of 24560125406


    .>2>.
   ⇗   ⇙v
  1   5 4
 ⇗   ⇙  v
.<6<.   .
^   v  ⇙
0   4 5
^   v⇙
.<6<.

05AB1E, 35 12 bytes

gIû¨•5ÏlÐθ•Λ

-23 bytes by porting emanresuA's Vyxal answer, so make sure to upvote that answer as well!

Try it online.

Original 35 bytes answer:

g¸ÞI¸Þð13ÅÉǝ•6´XdªÅ~ßмñD>γ•₃в)øvy`Λ

Try it online.

Explanation:

g               # Push the length of the (implicit) input-string
 I              # Push the input-string
  û             # Palindromize it
   ¨            # And then remove the last character
    •5ÏlÐθ•     # Push compressed integer 24560125406
           Λ    # Use the Canvas builtin with those three arguments
                # (which is output immediately as result)
g               # Push the length of the (implicit) input-string
 ¸Þ             # Wrap it into a list, and repeat it infinitely
I               # Push the input-string
 ¸Þ             # Repeat it infinitely as well
   ð            # Push a space character " "
    13ÅÉ        # Push a list of odd integers <= 13: [1,3,5,7,9,11,13]
        ǝ       # Insert spaces at those 0-based indices into the infinite list
•6´XdªÅ~ßмñD>γ• # Push compressed integer 506660546965149280261611581961
  ₃в            # Convert it to base-95 as list:
                #  [1,8,84,80,2,80,5,82,0,82,5,85,2,85,0,86]
)               # Wrap all three lists on the stack into a list
 ø              # Zip/transpose; swapping rows/columns,
                # which also discards the infinite portions
  vy            # Loop over each triplet:
    `           #  Pop and push the three values in the triplet to the stack
     Λ          #  Use the Canvas builtin with these three values as arguments
                #  (which is output immediately every iteration)

See this 05AB1E tip of mine (sections How to compress large integers? and How to compress integer lists?) to understand why •6´XdªÅ~ßмñD>γ• is 506660546965149280261611581961 and •6´XdªÅ~ßмñD>γ•₃в is [1,8,84,80,2,80,5,82,0,82,5,85,2,85,0,86].

Additional explanation of the Canvas builtin Λ/:

It takes 3 arguments to draw an ASCII shape:

  1. Length of the lines we want to draw
  2. Character/string to draw
  3. The direction to draw in, where each digit represents a certain direction:
7   0   1
  ↖ ↑ ↗
6 ← X → 2
  ↙ ↓ ↘
5   4   3

And there are certain special 'directions': +×8.

g16иI16иð13ÅÉǝ•6´XdªÅ~ßмñD>γ•₃в)ø with input Hello, world! creates the following list of Canvas arguments:

[[13,"Hello, world!",1],
 [13," ",8],
 [13,"Hello, world!",84],
 [13," ",80],
 [13,"Hello, world!",2],
 [13," ",80],
 [13,"Hello, world!",5],
 [13," ",82],
 [13,"Hello, world!",0],
 [13," ",82],
 [13,"Hello, world!",5],
 [13," ",85],
 [13,"Hello, world!",2],
 [13," ",85],
 [13,"Hello, world!",0],
 [13,"Hello, world!",86]]
  1. The first 13,"Hello, world!",1 will draw (all) 13 characters of string "Hello, world!" in direction 1/↗: try just this first step online;
  2. The second 13," ",8 will with special direction 8 reset back to the origin (the 13 and " " are ignored): try just the first two steps online;
  3. The third 13,"Hello, world!",84 will first reset back to the origin with direction 8 (step 2 is kinda a no-op to save a byte on the 13ÅÉ), and then again draw (all) 13 characters of string "Hello, world!" in direction 4/↓: try just the first three steps online;
  4. The fourth 13," ",80 will first reset back to the origin with direction 8 again, and then draw 13 space characters in direction 0/↑: try just the first four steps online;
  5. The fifth 13,"Hello, world!",2 will then continuing from that position to draw (all) 13 characters of string "Hello, world!" in direction 2/→: try just the first five steps online;
  6. etc. etc.

See this 05AB1E tip of mine to learn more about the Canvas builtin.

Pyth, 78 bytes

AtBtlQJ_SH
+*dGQVJs[*Nd@Q-GN*dHK@QN*d-HNK;
++_Q*dHhQVJs[@QN*dH@Q-GN*dtN@Q-GN;

With trailing newline. Inspired by Joshua de Haan's Python 3 answer.

Try it online here!

Java 7, 283 bytes

void a(String s){int h=s.length(),n=h*2-1,t=n-h,u=n-1;char[][]c=new char[n][n];for(int i=0;i<h;i++){c[0][t+i]=c[i][t-i]=c[t][t-i]=c[t+i][t]=c[t+i][u-i]=c[t-i][t+i]=c[t-i][u]=c[u][i]=c[u-i][0]=s.charAt(i);}for(int y=0;y<n;y++){System.out.println(new String(c[y]).replace('\0',' '));}}

Try it here!

Ungolfed:

void a(String s) {
    int length=s.length(),
        n=length*2-1,
        mid=n-length,
        doubleMid=n-1;
    char[][]c=new char[n][n];
    for(int i=0;i<length;i++) {
        c[0][mid+i]= 
        c[i][mid-i]=
        c[mid][mid-i]=
        c[mid+i][mid]=
        c[mid+i][doubleMid-i]=
        c[mid-i][mid+i]=
        c[mid-i][doubleMid]=
        c[doubleMid][i]=
        c[doubleMid-i][0]=s.charAt(i);
    }
    for(int y=0;y<n;y++){
        System.out.println(new String(c[y]).replace('\0',' '));
    }
}

Ruby, 148 144 bytes

+1 byte from the n flag. Shows newlines instead of semicolons for readability (same functionality).

S=" "
X=S*s=$_.size-2
puts X+S+I=$_,(r=1..s).map{|i|c=I[~i];S*(s-i+1)+I[i]+X+c+S*~-i+c},I.reverse+X+I[0],r.map{|i|c=I[i];I[~i]+X+c+S*(s-i)+c},I

Run like so. Input is a line of STDIN, with no trailing newline, so it likely needs to be piped from file.

ruby -ne 'S=" ";X=S*s=$_.size-2;puts X+S+I=$_,(r=1..s).map{|i|c=I[~i];S*(s-i+1)+I[i]+X+c+S*~-i+c},I.reverse+X+I[0],r.map{|i|c=I[i];I[~i]+X+c+S*(s-i)+c},I'

Javascript, 225 198 bytes

Saved 27 bytes thanks to @Neil

f=(s,l=s.length-2,d=' ',r='repeat',t=d[r](l))=>[d+t+s,...a=[...Array(l)].map((_,i)=>d[r](l-i)+s[i+1]+t+(p=s[l-i])+d[r](i)+p),[...s].reverse().join``+t+s[0],...a.map(v=>v.trim()).reverse(),s].join`
`

Original answer:

f=s=>{l=s.length,d=' ',r='repeat',a=[],t=d[r](l-2)+s;for(i=1;i++<l-1;)a.push(d[r](l-i)+s[i-1]+d[r](l-2)+(p=s[l-i])+d[r](i-2)+p);console.log(d+[t].concat(a,[...t].reverse().join``+s[0],a.map(v=>v.trim()).reverse(),s).join`
`)}

Python 2, 228 223 221 203 199 195 189

t=input()
x=" "
l=len(t)-1
q=l-1
f=range(q,0,-1)
print x*l+t
for i in f:print x*i+t[l-i]+x*q+t[i]+x*(q-i)+t[i]
print t[::-1]+x*q+t[0]
for i in f:print t[i]+x*q+t[l-i]+x*(i-1)+t[l-i]
print t

Python 3, 192 188 182

t=input()
x=" "
l=len(t)-1
q=l-1
p=print
f=range(q,0,-1)
p(x*l+t)
for i in f:p(x*i+t[l-i]+x*q+t[i]+x*(q-i)+t[i])
p(t[::-1]+x*q+t[0])
for i in f:p(t[i]+x*q+t[l-i]+x*(i-1)+t[l-i])
p(t)

x86 (IA-32) machine code, 126 bytes

Hexdump:

60 8b f9 57 33 c0 f2 ae 5e 2b fe 4f 87 fa 8d 1c
12 8b c3 48 f6 e3 c6 04 07 00 48 c6 04 07 20 75
f9 8b ea 4d 53 8d 04 2a 50 53 8b c5 f6 e3 8d 44
68 01 50 53 2b c2 8b c8 50 4b 53 55 53 03 c5 50
f7 d3 53 50 53 95 f6 e2 6b c0 04 50 43 53 51 6a
01 4a 52 6a 01 50 6a ff 51 b0 0a 6a 0b 8b dc 59
8b 6c cb fc 88 04 2f 03 2c cb 89 6c cb fc 83 f9
0a 75 01 ac e2 ea 4a 79 e0 83 c4 58 61 c3

This is a bit long, so to explain it I'll give C code first:

void doit(const char* s, char out[])
{
    int n = strlen(s);
    int w = 2 * n;
    int h = w - 1;
    int m = n - 1;

    memset(out, ' ', h * w);
    out[h * w] = 0;

    int offset1 = n + m;
    int offset2 = w * m + 2 * m + 1; // 2 * n * n - 1
    int offset3 = offset2 - n; // 2 * n * n - n - 1
    int offset4 = 4 * n * m; // 4 * n * n - 4 * n

    int offsets[] = {
        offset3, -1,
        offset4, 1,
        m, 1,
        offset3, 1 - w,
        offset4, -w,
        offset2 - 1, -w,
        offset2 - 1, w - 1,
        m, w - 1,
        offset3, w,
        offset2, w,
        offset1, w,
    };

    do
    {
        char c = *s++;
        for (int i = 0; i < 11; ++i)
        {
            if (i == 9)
                c = '\n';
            int offset = offsets[i * 2];
            assert(offset > 0 && offset < w * h);
            out[offset] = c;
            offsets[i * 2] += offsets[i * 2 + 1];
        }
    } while (--n);
}

Here n is the length of the input string.

The dimensions of the output area are 2n (width) by 2n-1 (height). First, it fills everything with spaces (and adds a terminating null byte). Then, it travels along 11 straight lines in the output area, and fills them with text:

Each line is represented by two numbers, a start offset and a stride. I stuffed them both into the array offsets, to make access "easy".

The interesting part is filling the array. There is little importance for the order of the entries in the array; I tried to rearrange them to minimize the number of register conflicts. In addition, quadratic formulas have some freedom in choosing the way of calculation; I tried to minimize the number of subtractions (because additions can be implemented by the flexible LEA instruction).

Assembly source:

    pushad;

    ; // Calculate the length of the input string
    mov edi, ecx;
    push edi;
    xor eax, eax;
    repne scasb;
    pop esi; // esi = input string
    sub edi, esi;
    dec edi;

    ; // Calculate the size of the output area
    xchg edi, edx;  // edx = n
                    // edi = output string
    lea ebx, [edx + edx]; // ebx = w
    mov eax, ebx;
    dec eax; // eax = h
    mul bl; // eax = w * h

    ; // Fill the output string with spaces and zero terminate it
    mov byte ptr [edi + eax], 0;
myfill:
    dec eax;
    mov byte ptr [edi + eax], ' ';
    jnz myfill;

    mov ebp, edx;
    dec ebp; // ebp = m

    ; // Fill the array of offsets
    push ebx; // w
    lea eax, [edx + ebp];
    push eax; // offset1
    push ebx; // w
    mov eax, ebp;
    mul bl;
    lea eax, [eax + 2 * ebp + 1];
    push eax; // offset2
    push ebx; // w
    sub eax, edx;
    mov ecx, eax; // ecx = offset3
    push eax; // offset3
    dec ebx;
    push ebx; // w - 1
    push ebp; // m
    push ebx; // w - 1
    add eax, ebp;
    push eax; // offset2 - 1
    not ebx;
    push ebx; // -w
    push eax; // offset2 - 1
    push ebx; // -w
    xchg eax, ebp; // eax = m
    mul dl;
    imul eax, eax, 4;
    push eax; // offset4
    inc ebx;
    push ebx; // 1 - w
    push ecx; // offset3
    push 1;
    dec edx; // edx = n - 1
    push edx;
    push 1;
    push eax;
    push -1;
    push ecx;

    ; // Use the array of offsets to write stuff to output
myout:
    mov al, '\n';
    push 11;
    mov ebx, esp;
    pop ecx;
myloop:
    mov ebp, [ebx + ecx * 8 - 4];
    mov [edi + ebp], al;
    add ebp, [ebx + ecx * 8];
    mov [ebx + ecx * 8 - 4], ebp;
    cmp ecx, 10;
    jne skip_read;
    lodsb;
skip_read:
    loop myloop;
    dec edx;
    jns myout;

    add esp, 11 * 8;

    popad;
    ret;

I used byte multiplications here, limiting the length of the input string to 127. This avoids clobbering the register edx - the product is calculated in ax instead.

A minor glitch: when filling the array, the length of the string gets decreased by 1. So I adjusted the loop exit condition:

    jns myout

It counts down to -1.